/*
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
* This program is free software licensed under GPL version 3
* Please see the included DOCS/LICENSE.md for more information
*/

#ifndef PLAYERMETHODS_H
#define PLAYERMETHODS_H

#include "Chat.h"
#include "GameTime.h"
#include "GossipDef.h"

/***
 * Inherits all methods from: [Object], [WorldObject], [Unit]
 */
namespace LuaPlayer
{
    /**
     * Returns `true` if the [Player] can Titan Grip, `false` otherwise.
     *
     * @return bool canTitanGrip
     */
    int CanTitanGrip(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanTitanGrip());
        return 1;
    }

    /**
     * Returns `true` if the [Player] has a talent by ID in specified spec, `false` otherwise.
     *
     * @param uint32 spellId : talent spellId to check
     * @param uint8 spec : specified spec. 0 for primary, 1 for secondary.
     * @return bool hasTalent
     */
    int HasTalent(lua_State* L, Player* player)
    {
        uint32 spellId = ALE::CHECKVAL<uint32>(L, 2);
        uint8 maxSpecs = MAX_TALENT_SPECS;
        uint8 spec = ALE::CHECKVAL<uint8>(L, 3);
        if (spec >= maxSpecs)
            return 1;
        ALE::Push(L, player->HasTalent(spellId, spec));
        return 1;
    }

    /**
     * Returns `true` if the [Player] has completed the specified achievement, `false` otherwise.
     *
     * @param uint32 achievementId
     * @return bool hasAchieved
     */
    int HasAchieved(lua_State* L, Player* player)
    {
        uint32 achievementId = ALE::CHECKVAL<uint32>(L, 2);
        ALE::Push(L, player->HasAchieved(achievementId));
        return 1;
    }

    /**
     * Returns the progress of the [Player] for the specified achievement criteria.
     *
     * @param uint32 criteriaId
     * @return uint32 progress : progress value or nil
     */
    int GetAchievementCriteriaProgress(lua_State* L, Player* player)
    {
        uint32 criteriaId = ALE::CHECKVAL<uint32>(L, 2);
        const AchievementCriteriaEntry* criteria = sAchievementCriteriaStore.LookupEntry(criteriaId);
        CriteriaProgress* progress = player->GetAchievementMgr()->GetCriteriaProgress(criteria);
        if (progress)
        {
            ALE::Push(L, progress->counter);
        }
        else
        {
            ALE::Push(L, (void*)nullptr);
        }
        return 1;
    }

    /**
     * Returns `true` if the [Player] has an active [Quest] by specific ID, `false` otherwise.
     *
     * @param uint32 questId
     * @return bool hasQuest
     */
    int HasQuest(lua_State* L, Player* player)
    {
        uint32 quest = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->IsActiveQuest(quest));
        return 1;
    }

    /**
     * Returns `true` if the [Player] has a skill by specific ID, `false` otherwise.
     *
     * @param uint32 skill
     * @return bool hasSkill
     */
    int HasSkill(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->HasSkill(skill));
        return 1;
    }

    /**
     * Returns `true` if the [Player] has a [Spell] by specific ID, `false` otherwise.
     *
     * @param uint32 spellId
     * @return bool hasSpell
     */
    int HasSpell(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->HasSpell(id));
        return 1;
    }

    /**
     * Returns true if [Player] has specified login flag
     *
     * @param uint32 flag
     * @return bool hasLoginFlag
     */
    int HasAtLoginFlag(lua_State* L, Player* player)
    {
        uint32 flag = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->HasAtLoginFlag((AtLoginFlags)flag));
        return 1;
    }

    /**
     * Returns true if [Player] has [Quest] for [GameObject]
     *
     * @param int32 entry : entry of a [GameObject]
     * @return bool hasQuest
     */
    int HasQuestForGO(lua_State* L, Player* player)
    {
        int32 entry = ALE::CHECKVAL<int32>(L, 2);

        ALE::Push(L, player->HasQuestForGO(entry));
        return 1;
    }

    /**
     * Returns `true` if the [Player] has a title by specific ID, `false` otherwise.
     *
     * @param uint32 titleId
     * @return bool hasTitle
     */
    int HasTitle(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);
        CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(id);
        if (titleInfo)
            ALE::Push(L, player->HasTitle(titleInfo));
        return 1;
    }
    
    /**
     * Returns `true` if the [Player] has the given amount of item entry specified, `false` otherwise.
     *
     * @param uint32 itemId : entry of the item
     * @param uint32 count = 1 : amount of items the player needs should have
     * @param bool check_bank = false : determines if the item can be in player bank
     * @return bool hasItem
     */
    int HasItem(lua_State* L, Player* player)
    {
        uint32 itemId = ALE::CHECKVAL<uint32>(L, 2);
        uint32 count = ALE::CHECKVAL<uint32>(L, 3, 1);
        bool check_bank = ALE::CHECKVAL<bool>(L, 4, false);
        ALE::Push(L, player->HasItemCount(itemId, count, check_bank));
        return 1;
    }
    
    /**
     * Returns `true` if the [Player] has a quest for the item entry specified, `false` otherwise.
     *
     * @param uint32 entry : entry of the item
     * @return bool hasQuest
     */
    int HasQuestForItem(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->HasQuestForItem(entry));
        return 1;
    }
    
    /**
     * Returns `true` if the [Player] can use the item or item entry specified, `false` otherwise.
     *
     * @proto canUse = (item)
     * @proto canUse = (entry)
     * @param [Item] item : an instance of an item
     * @param uint32 entry : entry of the item
     * @return bool canUse
     */
    int CanUseItem(lua_State* L, Player* player)
    {
        Item* item = ALE::CHECKOBJ<Item>(L, 2, false);
        if (item)
            ALE::Push(L, player->CanUseItem(item) == EQUIP_ERR_OK);
        else
        {
            uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
            const ItemTemplate* temp = eObjectMgr->GetItemTemplate(entry);
            if (temp)
                ALE::Push(L, player->CanUseItem(temp) == EQUIP_ERR_OK);
            else
                ALE::Push(L, false);
        }
        return 1;
    }

    /**
     * Returns `true` if the [Spell] specified by ID is currently on cooldown for the [Player], `false` otherwise.
     *
     * @param uint32 spellId
     * @return bool hasSpellCooldown
     */
    int HasSpellCooldown(lua_State* L, Player* player)
    {
        uint32 spellId = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->HasSpellCooldown(spellId));
        return 1;
    }

    /**
     * Returns `true` if the [Player] can share [Quest] specified by ID, `false` otherwise.
     *
     * @param uint32 entryId
     * @return bool hasSpellCooldown
     */
    int CanShareQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->CanShareQuest(entry));
        return 1;
    }

    /**
     * Returns `true` if the [Player] can currently communicate through chat, `false` otherwise.
     *
     * @return bool canSpeak
     */
    int CanSpeak(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanSpeak());
        return 1;
    }

    /**
     * Returns `true` if the [Player] has permission to uninvite others from the current group, `false` otherwise.
     *
     * @return bool canUninviteFromGroup
     */
    int CanUninviteFromGroup(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanUninviteFromGroup() == ERR_PARTY_RESULT_OK);
        return 1;
    }

    /**
     * Returns `true` if the [Player] can fly, `false` otherwise.
     *
     * @return bool canFly
     */
    int CanFly(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanFly());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently in water, `false` otherwise.
     *
     * @return bool isInWater
     */
    int IsInWater(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsInWater());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently moving, `false` otherwise.
     *
     * @return bool isMoving
     */
    int IsMoving(lua_State* L, Player* player) // enable for unit when mangos support it
    {
        ALE::Push(L, player->isMoving());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently flying, `false` otherwise.
     *
     * @return bool isFlying
     */
    int IsFlying(lua_State* L, Player* player) // enable for unit when mangos support it
    {
        ALE::Push(L, player->IsFlying());
        return 1;
    }

    /**
     * Returns `true` if the [Player] has a Tank Specialization, `false` otherwise.
     *
     * @return bool HasTankSpec
     */
    int HasTankSpec(lua_State* L, Player* player)
    {
        ALE::Push(L, player->HasTankSpec());
        return 1;
    }
    
    /**
     * Returns `true` if the [Player] has a Melee Specialization, `false` otherwise.
     *
     * @return bool HasMeleeSpec
     */
    int HasMeleeSpec(lua_State* L, Player* player)
    {
        ALE::Push(L, player->HasMeleeSpec());
        return 1;
    }
    
    /**
     * Returns `true` if the [Player] has a Caster Specialization, `false` otherwise.
     *
     * @return bool HasCasterSpec
     */
    int HasCasterSpec(lua_State* L, Player* player)
    {
        ALE::Push(L, player->HasCasterSpec());
        return 1;
    }
    
    /**
     * Returns `true` if the [Player] has a Heal Specialization, `false` otherwise.
     *
     * @return bool HasHealSpec
     */
    int HasHealSpec(lua_State* L, Player* player)
    {
        ALE::Push(L, player->HasHealSpec());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is in a [Group], `false` otherwise.
     *
     * @return bool isInGroup
     */
    int IsInGroup(lua_State* L, Player* player)
    {
        ALE::Push(L, (player->GetGroup() != NULL));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is in a [Guild], `false` otherwise.
     *
     * @return bool isInGuild
     */
    int IsInGuild(lua_State* L, Player* player)
    {
        ALE::Push(L, (player->GetGuildId() != 0));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is a Game Master, `false` otherwise.
     *
     * Note: This is only true when GM tag is activated! For alternative see [Player:GetGMRank]
     *
     * @return bool isGM
     */
    int IsGM(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsGameMaster());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is in an arena team specified by type, `false` otherwise.
     *
     * @param uint32 type
     * @return bool isInArenaTeam
     */
    int IsInArenaTeam(lua_State* L, Player* player)
    {
        uint32 type = ALE::CHECKVAL<uint32>(L, 2);
        if (type < MAX_ARENA_SLOT && player->GetArenaTeamId(type))
            ALE::Push(L, true);
        else
            ALE::Push(L, false);
        return 1;
    }

    /**
     * Returns `true` if the [Player] is immune to everything.
     *
     * @return bool isImmune
     */
    int IsImmuneToDamage(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isTotalImmune());
        return 1;
    }

    /**
     * Returns `true` if the [Player] satisfies all requirements to complete the quest entry.
     *
     * @param uint32 questId
     * @return bool canCompleteRepeatableQuest
     */
    int CanCompleteRepeatableQuest(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        const Quest* quest = sObjectMgr->GetQuestTemplate(questId); // Retrieve the Quest object
        if (!quest)
        {
            ALE::Push(L, false);
            return 1;
        }

        ALE::Push(L, player->CanCompleteRepeatableQuest(quest));
        return 1;
    }

    /**
     * Returns `true` if the [Player] satisfies all requirements to reward the quest entry.
     *
     * @param uint32 questId
     * @return bool canRewardQuest
     */
    int CanRewardQuest(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        const Quest* quest = sObjectMgr->GetQuestTemplate(questId); // Retrieve the Quest object
        if (!quest)
        {
            ALE::Push(L, false);
            return 1;
        }

        ALE::Push(L, player->CanRewardQuest(quest, true)); // Modify the second argument as needed
        return 1;
    }

    /**
     * Returns `true` if the [Player] satisfies all requirements to complete the quest entry.
     *
     * @param uint32 entry
     * @return bool canComplete
     */
    int CanCompleteQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->CanCompleteQuest(entry));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is a part of the Horde faction, `false` otherwise.
     *
     * @return bool isHorde
     */
    int IsHorde(lua_State* L, Player* player)
    {
        ALE::Push(L, (player->GetTeamId() == TEAM_HORDE));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is a part of the Alliance faction, `false` otherwise.
     *
     * @return bool isAlliance
     */
    int IsAlliance(lua_State* L, Player* player)
    {
        ALE::Push(L, (player->GetTeamId() == TEAM_ALLIANCE));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is 'Do Not Disturb' flagged, `false` otherwise.
     *
     * @return bool isDND
     */
    int IsDND(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isDND());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is 'Away From Keyboard' flagged, `false` otherwise.
     *
     * @return bool isAFK
     */
    int IsAFK(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isAFK());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently falling, `false` otherwise.
     *
     * @return bool isFalling
     */
    int IsFalling(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsFalling());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is in the same group and visible to the specified [Player], `false` otherwise.
     *
     * @param [Player] player : the source player
     * @param [Player] target : the player to check visibility from
     * @return bool isGroupVisible
     */
    int IsGroupVisibleFor(lua_State* L, Player* player)
    {
        Player* target = ALE::CHECKOBJ<Player>(L, 2);
        ALE::Push(L, player->IsGroupVisibleFor(target));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently in the same raid as another [Player] by object, `false` otherwise.
     *
     * @param [Player] player
     * @return bool isInSameRaidWith
     */
    int IsInSameRaidWith(lua_State* L, Player* player)
    {
        Player* target = ALE::CHECKOBJ<Player>(L, 2);
        ALE::Push(L, player->IsInSameRaidWith(target));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently in the same [Group] as another [Player] by object, `false` otherwise.
     *
     * @param [Player] player
     * @return bool isInSameGroupWith
     */
    int IsInSameGroupWith(lua_State* L, Player* player)
    {
        Player* target = ALE::CHECKOBJ<Player>(L, 2);
        ALE::Push(L, player->IsInSameGroupWith(target));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is eligible for Honor or XP gain by [Unit] specified, `false` otherwise.
     *
     * @param [Unit] unit
     * @return bool isHonorOrXPTarget
     */
    int IsHonorOrXPTarget(lua_State* L, Player* player)
    {
        Unit* victim = ALE::CHECKOBJ<Unit>(L, 2);

        ALE::Push(L, player->isHonorOrXPTarget(victim));
        return 1;
    }

    /**
     * Returns `true` if the [Player] can see anoter [Player] specified by object, `false` otherwise.
     *
     * @param [Player] player
     * @return bool isVisibleForPlayer
     */
    int IsVisibleForPlayer(lua_State* L, Player* player)
    {
        Player* target = ALE::CHECKOBJ<Player>(L, 2);

        ALE::Push(L, player->IsVisibleGloballyFor(target));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently visible to other players, `false` if hidden via GM invisibility.
     *
     * @param [Player] player
     * @return bool isVisible
     */
    int IsGMVisible(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isGMVisible());
        return 1;
    }

    /**
     * Returns `true` if the [Player] has taxi cheat activated, `false` otherwise.
     *
     * @return bool isTaxiCheater
     */
    int IsTaxiCheater(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isTaxiCheater());
        return 1;
    }

    /**
     * Returns `true` if the [Player] has GM chat enabled, `false` otherwise.
     *
     * @param [Player] player
     * @return bool isGMChat
     */
    int IsGMChat(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isGMChat());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is accepting whispers, `false` otherwise.
     *
     * @return bool isAcceptingWhispers
     */
    int IsAcceptingWhispers(lua_State* L, Player* player)
    {
        ALE::Push(L, player->isAcceptWhispers());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently rested, `false` otherwise.
     *
     * @return bool isRested
     */
    int IsRested(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetRestBonus() > 0.0f);
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently in a [BattleGround] queue, `false` otherwise.
     *
     * @return bool inBattlegroundQueue
     */
    int InBattlegroundQueue(lua_State* L, Player* player)
    {
        ALE::Push(L, player->InBattlegroundQueue());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently in an arena, `false` otherwise.
     *
     * @return bool inArena
     */
    int InArena(lua_State* L, Player* player)
    {
        ALE::Push(L, player->InArena());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is currently in a [BattleGround], `false` otherwise.
     *
     * @return bool inBattleGround
     */
    int InBattleground(lua_State* L, Player* player)
    {
        ALE::Push(L, player->InBattleground());
        return 1;
    }

    /**
     * Returns `true` if the [Player] can block incomming attacks, `false` otherwise.
     *
     * @return bool canBlock
     */
    int CanBlock(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanBlock());
        return 1;
    }

    /**
     * Returns `true` if the [Player] can parry incomming attacks, `false` otherwise.
     *
     * @return bool canParry
     */
    int CanParry(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanParry());
        return 1;
    }

    /**
     * Returns the amount of available specs the [Player] currently has
     *
     * @return uint8 specCount
     */
    int GetSpecsCount(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSpecsCount());
        return 1;
    }

    /**
     * Returns the [Player]s active spec ID
     *
     * @return uint32 specId
     */
    int GetActiveSpec(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetActiveSpec());
        return 1;
    }

    /**
     * Returns the normal phase of the player instead of the actual phase possibly containing GM phase
     *
     * @return uint32 phasemask
     */
    int GetPhaseMaskForSpawn(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetPhaseMaskForSpawn());
        return 1;
    }

    /**
     * Returns the [Player]s current amount of Achievement Points
     *
     * @return uint32 achievementPoints
     */
    int GetAchievementPoints(lua_State* L, Player* player)
    {
        uint32 count = 0;
        const CompletedAchievementMap& completedAchievements = player->GetAchievementMgr()->GetCompletedAchievements();
        for (auto& pair : completedAchievements)
        {
            AchievementEntry const* achievement = sAchievementStore.LookupEntry(pair.first);
            if (achievement)
            {
                count += achievement->points;
            }
        }

        ALE::Push(L, count);
        return 1;
    }

    /**
     * Returns the [Player]s current amount of Achievements Completed
     *
     * @return uint32 achievementsCount
     */
    int GetCompletedAchievementsCount(lua_State* L, Player* player)
    {
        uint32 count = 0;
        bool countFeatsOfStrength = ALE::CHECKVAL<bool>(L, 2, false);
        const CompletedAchievementMap& completedAchievements = player->GetAchievementMgr()->GetCompletedAchievements();
        for (auto& pair : completedAchievements)
        {
            AchievementEntry const* achievement = sAchievementStore.LookupEntry(pair.first);
            if (achievement && (achievement->categoryId != 81 || countFeatsOfStrength))
            {               
                    count++;             
            }
        }

        ALE::Push(L, count);
        return 1;
    }

    /**
     * Returns the [Player]s current amount of Arena Points
     *
     * @return uint32 arenaPoints
     */
    int GetArenaPoints(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetArenaPoints());
        return 1;
    }

    /**
     * Returns the [Player]s current amount of Honor Points
     *
     * @return uint32 honorPoints
     */
    int GetHonorPoints(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetHonorPoints());
        return 1;
    }

    /**
     * Returns the [Player]s current shield block value
     *
     * @return uint32 blockValue
     */
    int GetShieldBlockValue(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetShieldBlockValue());
        return 1;
    }

    /**
     * Returns the [Player]s cooldown delay by specified [Spell] ID
     *
     * @param uint32 spellId
     * @return uint32 spellCooldownDelay
     */
    int GetSpellCooldownDelay(lua_State* L, Player* player)
    {
        uint32 spellId = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, uint32(player->GetSpellCooldownDelay(spellId)));
        return 1;
    }

    /**
     * Returns the [Player]s current latency in MS
     *
     * @return uint32 latency
     */
    int GetLatency(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetLatency());
        return 1;
    }

    /**
     * Returns the faction ID the [Player] is currently flagged as champion for
     *
     * @return uint32 championingFaction
     */
    int GetChampioningFaction(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetChampioningFaction());
        return 1;
    }

    /**
     * Returns [Player]s original sub group
     *
     * @return uint8 subGroup
     */
    int GetOriginalSubGroup(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetOriginalSubGroup());
        return 1;
    }

    /**
     * Returns [Player]s original [Group] object
     *
     * @return [Group] group
     */
    int GetOriginalGroup(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetOriginalGroup());
        return 1;
    }

    /**
     * Returns a random Raid Member [Player] object within radius specified of [Player]
     *
     * @param float radius
     * @return [Player] player
     */
    int GetNextRandomRaidMember(lua_State* L, Player* player)
    {
        float radius = ALE::CHECKVAL<float>(L, 2);

        ALE::Push(L, player->GetNextRandomRaidMember(radius));
        return 1;
    }

    /**
     * Returns [Player]s current sub group
     *
     * @return uint8 subGroup
     */
    int GetSubGroup(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSubGroup());
        return 1;
    }

    /**
     * Returns [Group] invitation
     *
     * @return [Group] group
     */
    int GetGroupInvite(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetGroupInvite());
        return 1;
    }

    /**
     * Returns the [Player]'s experience points
     *
     * @return uint32 xp
     */
    int GetXP(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetUInt32Value(PLAYER_XP));
        return 1;
    }

    /**
     * Returns rested experience bonus
     *
     * @param uint32 xp
     * @return uint32 xpBonus
     */
    int GetXPRestBonus(lua_State* L, Player* player)
    {
        uint32 xp = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetXPRestBonus(xp));
        return 1;
    }

    /**
     * Returns the [Player]s current [BattleGround] type ID
     *
     * @return [BattleGroundTypeId] typeId
     */
    int GetBattlegroundTypeId(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetBattlegroundTypeId());
        return 1;
    }

    /**
     * Returns the [Player]s current [BattleGround] ID
     *
     * @return uint32 battleGroundId
     */
    int GetBattlegroundId(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetBattlegroundId());
        return 1;
    }

    /**
     * Returns the [Player]s reputation rank of faction specified
     *
     * @param uint32 faction
     * @return [ReputationRank] rank
     */
    int GetReputationRank(lua_State* L, Player* player)
    {
        uint32 faction = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetReputationRank(faction));
        return 1;
    }

    /**
     * Returns the [Player]s current level of intoxication
     *
     * @return uint16 drunkValue
     */
    int GetDrunkValue(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetDrunkValue());
        return 1;
    }

    /**
     * Returns skill temporary bonus value
     *
     * @param uint32 skill
     * @param int16 bonusVal
     */
    int GetSkillTempBonusValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetSkillTempBonusValue(skill));
        return 1;
    }

    /**
     * Returns skill permanent bonus value
     *
     * @param uint32 skill
     * @param int16 bonusVal
     */
    int GetSkillPermBonusValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetSkillPermBonusValue(skill));
        return 1;
    }

    /**
     * Returns skill value without bonus'
     *
     * @param uint32 skill
     * @return uint16 pureVal
     */
    int GetPureSkillValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetPureSkillValue(skill));
        return 1;
    }

    /**
     * Returns base skill value
     *
     * @param uint32 skill
     * @return uint16 baseVal
     */
    int GetBaseSkillValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetBaseSkillValue(skill));
        return 1;
    }

    /**
     * Returns skill value
     *
     * @param uint32 skill
     * @return uint16 val
     */
    int GetSkillValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetSkillValue(skill));
        return 1;
    }

    /**
     * Returns max value of specified skill without bonus'
     *
     * @param uint32 skill
     * @return uint16 pureVal
     */
    int GetPureMaxSkillValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetPureMaxSkillValue(skill));
        return 1;
    }

    /**
     * Returns max value of specified skill
     *
     * @param uint32 skill
     * @return uint16 val
     */
    int GetMaxSkillValue(lua_State* L, Player* player)
    {
        uint32 skill = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetMaxSkillValue(skill));
        return 1;
    }

    /**
     * Returns mana bonus from amount of intellect
     *
     * @return float bonus
     */
    int GetManaBonusFromIntellect(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetManaBonusFromIntellect());
        return 1;
    }

    /**
     * Returns health bonus from amount of stamina
     *
     * @return float bonus
     */
    int GetHealthBonusFromStamina(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetHealthBonusFromStamina());
        return 1;
    }

    /**
     * Returns raid or dungeon difficulty
     *
     * @param bool isRaid = true : argument is TrinityCore only
     * @return int32 difficulty
     */
    int GetDifficulty(lua_State* L, Player* player)
    {
        bool isRaid = ALE::CHECKVAL<bool>(L, 2, true);
        ALE::Push(L, player->GetDifficulty(isRaid));
        return 1;
    }

    /**
     * Returns the [Player]s current guild rank
     *
     * @return uint32 guildRank
     */
    int GetGuildRank(lua_State* L, Player* player) // TODO: Move to Guild Methods
    {
        ALE::Push(L, player->GetRank());
        return 1;
    }

    /**
     * Returns the [Player]s free talent point amount
     *
     * @return uint32 freeTalentPointAmt
     */
    int GetFreeTalentPoints(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetFreeTalentPoints());
        return 1;
    }

    /**
     * Returns the name of the [Player]s current [Guild]
     *
     * @return string guildName
     */
    int GetGuildName(lua_State* L, Player* player)
    {
        if (!player->GetGuildId())
            return 1;
        ALE::Push(L, eGuildMgr->GetGuildNameById(player->GetGuildId()));
        return 1;
    }

    /**
     * Returns the amount of reputation the [Player] has with the faction specified
     *
     * @param uint32 faction
     * @return int32 reputationAmt
     */
    int GetReputation(lua_State* L, Player* player)
    {
        uint32 faction = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetReputationMgr().GetReputation(faction));
        return 1;
    }

    /**
     * Returns [Unit] target combo points are on
     *
     * @return [Unit] target
     */
    int GetComboTarget(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetComboTarget());
        return 1;
    }

    /**
     * Returns [Player]'s combo points
     *
     * @return uint8 comboPoints
     */
    int GetComboPoints(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetComboPoints());
        return 1;
    }

    /**
     * Returns the amount of time the [Player] has spent ingame
     *
     * @return uint32 inGameTime
     */
    int GetInGameTime(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetInGameTime());
        return 1;
    }

    /**
     * Returns the status of the [Player]s [Quest] specified by entry ID
     *
     * @param uint32 questId
     * @return [QuestStatus] questStatus
     */
    int GetQuestStatus(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetQuestStatus(entry));
        return 1;
    }

    /**
     * Returns `true` if the [Player]s [Quest] specified by entry ID has been rewarded, `false` otherwise.
     *
     * @param uint32 questId
     * @return bool questRewardStatus
     */
    int GetQuestRewardStatus(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetQuestRewardStatus(questId));
        return 1;
    }

    /**
     * Returns [Quest] required [Creature] or [GameObject] count
     *
     * @param uint32 quest : entry of a quest
     * @param int32 entry : entry of required [Creature]
     * @return uint16 count
     */
    int GetReqKillOrCastCurrentCount(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        int32 entry = ALE::CHECKVAL<int32>(L, 3);

        ALE::Push(L, player->GetReqKillOrCastCurrentCount(questId, entry));
        return 1;
    }

    /**
     * Returns the quest level of the [Player]s [Quest] specified by object
     *
     * @param uint32 questId
     * @return [QuestStatus] questRewardStatus
     */
    int GetQuestLevel(lua_State* L, Player* player)
    {
        Quest* quest = ALE::CHECKOBJ<Quest>(L, 2);

        ALE::Push(L, player->GetQuestLevel(quest));
        return 1;
    }

    /**
     * Returns a [Player]s [Item] object by gear slot specified
     *
     * @param uint8 slot
     * @return [Item] item
     */
    int GetEquippedItemBySlot(lua_State* L, Player* player)
    {
        uint8 slot = ALE::CHECKVAL<uint8>(L, 2);
        if (slot >= EQUIPMENT_SLOT_END)
            return 1;

        Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
        ALE::Push(L, item);
        return 1;
    }

    /**
     * Returns the [Player]s current resting bonus
     *
     * @return float restBonus
     */
    int GetRestBonus(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetRestBonus());
        return 1;
    }

    /**
     * Returns active GM chat tag
     *
     * @return uint8 tag
     */
    int GetChatTag(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetChatTag());
        return 1;
    }

    /**
     * Returns an item in given bag on given slot.
     *
     * <pre>
     * Possible and most commonly used combinations:
     *
     * bag = 255
     * slots 0-18 equipment
     * slots 19-22 equipped bag slots
     * slots 23-38 backpack
     * slots 39-66 bank main slots
     * slots 67-74 bank bag slots
     * slots 86-117 keyring
     *
     * bag = 19-22
     * slots 0-35 for equipped bags
     *
     * bag = 67-74
     * slots 0-35 for bank bags
     * </pre>
     *
     * @param uint8 bag : the bag the [Item] is in, you can get this with [Item:GetBagSlot]
     * @param uint8 slot : the slot the [Item] is in within the bag, you can get this with [Item:GetSlot]
     * @return [Item] item : [Item] or nil
     */
    int GetItemByPos(lua_State* L, Player* player)
    {
        uint8 bag = ALE::CHECKVAL<uint8>(L, 2);
        uint8 slot = ALE::CHECKVAL<uint8>(L, 3);

        ALE::Push(L, player->GetItemByPos(bag, slot));
        return 1;
    }

    /**
     * Returns an [Item] from the player by guid.
     *
     * The item can be equipped, in bags or in bank.
     *
     * @param ObjectGuid guid : an item guid
     * @return [Item] item
     */
    int GetItemByGUID(lua_State* L, Player* player)
    {
        ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);

        ALE::Push(L, player->GetItemByGuid(guid));
        return 1;
    }

    /**
     * Returns the amount of mails in the player's mailbox.
     *
     * @return uint32 mailCount
     */
    int GetMailCount(lua_State* L, Player* player)
    {
        const CharacterCacheEntry* cache = sCharacterCache->GetCharacterCacheByGuid(player->GetGUID());
        if (cache)
        {
            ALE::Push(L, static_cast<uint32>(cache->MailCount));
        }
        else
        {
            ALE::Push(L, player->GetMailSize());
        }

        return 1;
    }

    /**
     * Returns a mailed [Item] by guid.
     *
     * @param ObjectGuid guid : an item guid
     * @return [Item] item
     */
    int GetMailItem(lua_State* L, Player* player)
    {
        ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2);

        ALE::Push(L, player->GetMItem(guid.GetCounter()));
        return 1;
    }

    /**
     * Returns an [Item] from the player by entry.
     *
     * The item can be equipped, in bags or in bank.
     *
     * @param uint32 entryId
     * @return [Item] item
     */
    int GetItemByEntry(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        ALE::Push(L, player->GetItemByEntry(entry));
        return 1;
    }
    
    /**
     * Returns the database textID of the [WorldObject]'s gossip header text for the [Player]
     *
     * @param [WorldObject] object
     * @return uint32 textId : key to npc_text database table
     */
    int GetGossipTextId(lua_State* L, Player* player)
    {
        WorldObject* obj = ALE::CHECKOBJ<WorldObject>(L, 2);
        ALE::Push(L, player->GetGossipTextId(obj));
        return 1;
    }

    /**
     * Returns the [Player]s currently selected [Unit] object
     *
     * @return [Unit] unit
     */
    int GetSelection(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSelectedUnit());
        return 1;
    }

    /**
     * Returns the [Player]s GM Rank
     *
     * @return [AccountTypes] gmRank
     */
    int GetGMRank(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetSecurity());
        return 1;
    }

    /**
     * Returns the [Player]s amount of money in copper
     *
     * @return uint32 coinage
     */
    int GetCoinage(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetMoney());
        return 1;
    }

    /**
     * Returns the [Player]s current [Guild] ID
     *
     * @return uint32 guildId
     */
    int GetGuildId(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetGuildId());
        return 1;
    }

    /**
     * Returns the [Player]s [TeamId]
     *
     * @return [TeamId] teamId
     */
    int GetTeam(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetTeamId());
        return 1;
    }
    
    /**
     * Returns amount of the specified [Item] the [Player] has.
     *
     * @param uint32 entry : entry of the item
     * @param bool checkinBank = false : also counts the items in player's bank if true
     * @return uint32 itemamount
     */
    int GetItemCount(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
        bool checkinBank = ALE::CHECKVAL<bool>(L, 3, false);
        ALE::Push(L, player->GetItemCount(entry, checkinBank));
        return 1;
    }

    /**
     * Returns the [Player]s lifetime Honorable Kills
     *
     * @return uint32 lifeTimeKils
     */
    int GetLifetimeKills(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
        return 1;
    }

    /**
     * Returns the [Player]s IP address
     *
     * @return string ip
     */
    int GetPlayerIP(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetRemoteAddress());
        return 1;
    }

    /**
     * Returns the [Player]s time played at current level
     *
     * @return uint32 currLevelPlayTime
     */
    int GetLevelPlayedTime(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetLevelPlayedTime());
        return 1;
    }

    /**
     * Returns the [Player]s total time played
     *
     * @return uint32 totalPlayTime
     */
    int GetTotalPlayedTime(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetTotalPlayedTime());
        return 1;
    }

    /**
     * Returns the [Player]s [Guild] object
     *
     * @return [Guild] guild
     */
    int GetGuild(lua_State* L, Player* player)
    {
        ALE::Push(L, eGuildMgr->GetGuildById(player->GetGuildId()));
        return 1;
    }

    /**
     * Returns the [Player]s [Group] object
     *
     * @return [Group] group
     */
    int GetGroup(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetGroup());
        return 1;
    }

    /**
     * Returns the [Player]s account ID
     *
     * @return uint32 accountId
     */
    int GetAccountId(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetAccountId());
        return 1;
    }

    /**
     * Returns the [Player]s account name
     *
     * @return string accountName
     */
    int GetAccountName(lua_State* L, Player* player)
    {
        std::string accName;
        if (AccountMgr::GetName(player->GetSession()->GetAccountId(), accName))
            ALE::Push(L, accName);
        return 1;
    }

    /**
     * Returns the [Player]s completed quest count
     *
     * @return int32 questcount
     */
    int GetCompletedQuestsCount(lua_State* L, Player* player)
    {
        uint32 count = player->GetRewardedQuestCount();

        ALE::Push(L, count);
        return 1;
    }

    /**
     * Returns the [Player]s [Corpse] object
     *
     * @return [Corpse] corpse
     */
    int GetCorpse(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetCorpse());
        return 1;
    }

    /**
     * Returns the [Player]s database locale index
     *
     * @return int localeIndex
     */
    int GetDbLocaleIndex(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetSessionDbLocaleIndex());
        return 1;
    }

    /**
     * Returns the [Player]s game client locale
     *
     * @return [LocaleConstant] locale
     */
    int GetDbcLocale(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetSessionDbcLocale());
        return 1;
    }

    /**
     * Returns known taxi nodes (flight paths) that the player has unlocked.
     *
     * @return table nodes : A table containing the IDs of the known taxi nodes
     */
    int GetKnownTaxiNodes(lua_State* L, Player* player)
    {
        if (!player)
            return 0;

        lua_newtable(L);

        ByteBuffer data;
        player->m_taxi.AppendTaximaskTo(data, false);

        for (uint8 i = 0; i < TaxiMaskSize; i++)
        {
            uint32 mask;
            data >> mask;

            for (uint8 bit = 0; bit < 32; bit++)
            {
                if (mask & (1 << bit))
                {
                    uint32 nodeId = (i * 32) + bit + 1;
                    lua_pushinteger(L, nodeId);
                    lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
                }
            }
        }

        return 1;
    }

    /*int GetRecruiterId(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSession()->GetRecruiterId());
        return 1;
    }*/

    /*int GetSelectedPlayer(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSelectedPlayer());
        return 1;
    }*/

    /*int GetSelectedUnit(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSelectedUnit());
        return 1;
    }*/

    /*int GetNearbyGameObject(lua_State* L, Player* player)
    {
        ALE::Push(L, ChatHandler(player->GetSession()).GetNearbyGameObject());
        return 1;
    }*/
    
    /**
     * Locks the player controls and disallows all movement and casting.
     *
     * @param bool apply = true : lock if true and unlock if false
     */
    int SetPlayerLock(lua_State* L, Player* player)
    {
        bool apply = ALE::CHECKVAL<bool>(L, 2, true);

        if (apply)
        {
            player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED | UNIT_FLAG_SILENCED);
            player->SetClientControl(player, 0);
        }
        else
        {
            player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED | UNIT_FLAG_SILENCED);
            player->SetClientControl(player, 1);
        }
        return 0;
    }

    /**
     * Sets the [Player]s login flag to the flag specified
     *
     * @param uint32 flag
     */
    int SetAtLoginFlag(lua_State* L, Player* player)
    {
        uint32 flag = ALE::CHECKVAL<uint32>(L, 2);

        player->SetAtLoginFlag((AtLoginFlags)flag);
        return 0;
    }

    /**
     * Sets the [Player]s sheathe state to the state specified
     *
     * @param uint32 sheatheState
     */
    int SetSheath(lua_State* L, Player* player)
    {
        uint32 sheathed = ALE::CHECKVAL<uint32>(L, 2);
        if (sheathed >= MAX_SHEATH_STATE)
            return 0;

        player->SetSheath((SheathState)sheathed);
        return 0;
    }

    /**
     * Sets the [Player]s intoxication level to the level specified
     *
     * @param uint8 drunkValue
     */
    int SetDrunkValue(lua_State* L, Player* player)
    {
        uint8 newDrunkValue = ALE::CHECKVAL<uint8>(L, 2);

        player->SetDrunkValue(newDrunkValue);
        return 0;
    }

    /**
     * Sets the [Player]s faction standing to that of the race specified
     *
     * @param uint8 raceId
     */
    int SetFactionForRace(lua_State* L, Player* player)
    {
        uint8 race = ALE::CHECKVAL<uint8>(L, 2);

        player->SetFactionForRace(race);
        return 0;
    }

    /**
     * Sets (increases) skill of the [Player]
     *
     * @param uint16 id
     * @param uint16 step
     * @param uint16 currVal
     * @param uint16 maxVal
     */
    int SetSkill(lua_State* L, Player* player)
    {
        uint16 id = ALE::CHECKVAL<uint16>(L, 2);
        uint16 step = ALE::CHECKVAL<uint16>(L, 3);
        uint16 currVal = ALE::CHECKVAL<uint16>(L, 4);
        uint16 maxVal = ALE::CHECKVAL<uint16>(L, 5);

        player->SetSkill(id, currVal, maxVal, step);
        return 0;
    }

    /**
     * Sets the [Player]s guild rank to the rank specified
     *
     * @param uint8 rank
     */
    int SetGuildRank(lua_State* L, Player* player) // TODO: Move to Guild Methods
    {
        uint8 rank = ALE::CHECKVAL<uint8>(L, 2);

        if (!player->GetGuildId())
            return 0;

        player->SetRank(rank);
        return 0;
    }

    /**
     * Sets the [Player]s free talent points to the amount specified for the current spec
     *
     * @param uint32 talentPointAmt
     */
    int SetFreeTalentPoints(lua_State* L, Player* player)
    {
        uint32 points = ALE::CHECKVAL<uint32>(L, 2);

        player->SetFreeTalentPoints(points);
        player->SendTalentsInfoData(false);
        return 0;
    }

    /**
     * Sets the [Player]s reputation amount for the faction specified
     *
     * @param uint32 factionId
     * @param int32 reputationValue
     */
    int SetReputation(lua_State* L, Player* player)
    {
        uint32 faction = ALE::CHECKVAL<uint32>(L, 2);
        int32 value = ALE::CHECKVAL<int32>(L, 3);

        FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
        player->GetReputationMgr().SetReputation(factionEntry, value);
        return 0;
    }

    /**
     * Sets [Quest] state
     *
     * @param uint32 entry : entry of a quest
     * @param uint32 status
     */
    int SetQuestStatus(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
        uint32 status = ALE::CHECKVAL<uint32>(L, 3);
        if (status >= MAX_QUEST_STATUS)
            return 0;

        player->SetQuestStatus(entry, (QuestStatus)status);
        return 0;
    }

    /**
     * Sets the [Player]s rest bonus to the amount specified
     *
     * @param float restBonus
     */
    int SetRestBonus(lua_State* L, Player* player)
    {
        float bonus = ALE::CHECKVAL<float>(L, 2);

        player->SetRestBonus(bonus);
        return 0;
    }

    /**
     * Toggles whether the [Player] accepts whispers or not
     *
     * @param bool acceptWhispers = true
     */
    int SetAcceptWhispers(lua_State* L, Player* player)
    {
        bool on = ALE::CHECKVAL<bool>(L, 2, true);

        player->SetAcceptWhispers(on);
        return 0;
    }

    /**
     * Toggles PvP Death
     *
     * @param bool on = true
     */
    int SetPvPDeath(lua_State* L, Player* player)
    {
        bool on = ALE::CHECKVAL<bool>(L, 2, true);

        player->SetPvPDeath(on);
        return 0;
    }

    /**
     * Toggles whether the [Player] has GM visibility on or off
     *
     * @param bool gmVisible = true
     */
    int SetGMVisible(lua_State* L, Player* player)
    {
        bool on = ALE::CHECKVAL<bool>(L, 2, true);

        player->SetGMVisible(on);
        return 0;
    }

    /**
     * Sets the player's known taxi nodes (flight paths).
     *
     * @param table nodes : A table containing the taxi node IDs to set as known
     */
    int SetKnownTaxiNodes(lua_State* L, Player* player)
    {
        if (!player)
            return 0;
    
        if (!lua_istable(L, 2))
            return 0;
    
        lua_pushnil(L);
    
        while (lua_next(L, 2) != 0)
        {
            uint32 nodeId = luaL_checkinteger(L, -1);
    
            if (nodeId > 0) 
                player->m_taxi.SetTaximaskNode(nodeId);
    
            lua_pop(L, 1);
        }
    
        return 0;
    }

    /**
     * Toggles whether the [Player] has taxi cheat enabled or not
     *
     * @param bool taxiCheat = true
     */
    int SetTaxiCheat(lua_State* L, Player* player)
    {
        bool on = ALE::CHECKVAL<bool>(L, 2, true);

        player->SetTaxiCheater(on);
        return 0;
    }

    /**
     * Toggle Blizz (GM) tag
     *
     * @param bool on = true
     */
    int SetGMChat(lua_State* L, Player* player)
    {
        bool on = ALE::CHECKVAL<bool>(L, 2, true);

        player->SetGMChat(on);
        return 0;
    }

    /**
     * Toggles the [Player]s GM mode on or off
     *
     * @param bool setGmMode = true
     */
    int SetGameMaster(lua_State* L, Player* player)
    {
        bool on = ALE::CHECKVAL<bool>(L, 2, true);

        player->SetGameMaster(on);
        return 0;
    }

    /**
     * Sets the [Player]s gender to gender specified
     *
     * - GENDER_MALE    = 0
     * - GENDER_FEMALE  = 1
     *
     * @param [Gender] gender
     */
    int SetGender(lua_State* L, Player* player)
    {
        uint32 _gender = ALE::CHECKVAL<uint32>(L, 2);

        Gender gender;
        switch (_gender)
        {
            case 0:
                gender = GENDER_MALE;
                break;
            case 1:
                gender = GENDER_FEMALE;
                break;
            default:
                return luaL_argerror(L, 2, "valid Gender expected");
        }

        player->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender);
        player->SetByteValue(PLAYER_BYTES_3, 0, gender);
        player->InitDisplayIds();
        return 0;
    }

    /**
     * Sets the [Player]s Arena Points to the amount specified
     *
     * @param uint32 arenaPoints
     */
    int SetArenaPoints(lua_State* L, Player* player)
    {
        uint32 arenaP = ALE::CHECKVAL<uint32>(L, 2);
        player->SetArenaPoints(arenaP);
        return 0;
    }

    /**
     * Sets the [Player]s Honor Points to the amount specified
     *
     * @param uint32 honorPoints
     */
    int SetHonorPoints(lua_State* L, Player* player)
    {
        uint32 honorP = ALE::CHECKVAL<uint32>(L, 2);
        player->SetHonorPoints(honorP);
        return 0;
    }

    /**
     * Sets the [Player]s amount of Lifetime Honorable Kills to the value specified
     *
     * @param uint32 honorableKills
     */
    int SetLifetimeKills(lua_State* L, Player* player)
    {
        uint32 val = ALE::CHECKVAL<uint32>(L, 2);
        player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, val);
        return 0;
    }

    /**
     * Sets the [Player]s amount of money to copper specified
     *
     * @param uint32 copperAmt
     */
    int SetCoinage(lua_State* L, Player* player)
    {
        uint32 amt = ALE::CHECKVAL<uint32>(L, 2);
        player->SetMoney(amt);
        return 0;
    }

    /**
     * Sets the [Player]s home location to the location specified
     *
     * @param float x : X Coordinate
     * @param float y : Y Coordinate
     * @param float z : Z Coordinate
     * @param uint32 mapId : Map ID
     * @param uint32 areaId : Area ID
     */
    int SetBindPoint(lua_State* L, Player* player)
    {
        float x = ALE::CHECKVAL<float>(L, 2);
        float y = ALE::CHECKVAL<float>(L, 3);
        float z = ALE::CHECKVAL<float>(L, 4);
        uint32 mapId = ALE::CHECKVAL<uint32>(L, 5);
        uint32 areaId = ALE::CHECKVAL<uint32>(L, 6);

        WorldLocation loc(mapId, x, y, z);
        player->SetHomebind(loc, areaId);
        return 0;
    }

    /**
     * Adds the specified title to the [Player]s list of known titles
     *
     * @param uint32 titleId
     */
    int SetKnownTitle(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);
        CharTitlesEntry const* t = sCharTitlesStore.LookupEntry(id);
        if (t)
            player->SetTitle(t, false);
        return 0;
    }

    /**
     * Adds the specified achievement to the [Player]s
     *
     * @param uint32 achievementid
     */
    int SetAchievement(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);
        AchievementEntry const* t = sAchievementStore.LookupEntry(id);
        if (t)
            player->CompletedAchievement(t);
        return 0;
    }

    /*int SetMovement(lua_State* L, Player* player)
    {
        int32 pType = ALE::CHECKVAL<int32>(L, 2);

        player->SetMovement((PlayerMovementType)pType);
        return 0;
    }*/

    /**
     * Reset the [Player]s completed achievements
     */
    int ResetAchievements(lua_State* /*L*/, Player* player)
    {
        player->ResetAchievements();
        return 0;
    }
    
    /**
     * Shows the mailbox window to the player from specified guid.
     *
     * @param ObjectGuid guid = playerguid : guid of the mailbox window sender
     */
    int SendShowMailBox(lua_State* L, Player* player)
    {
        ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 2, player->GET_GUID());

        player->GetSession()->SendShowMailBox(guid);
        return 0;
    }

    /**
     * Adds or detracts from the [Player]s current Arena Points
     *
     * @param int32 amount
     */
    int ModifyArenaPoints(lua_State* L, Player* player)
    {
        int32 amount = ALE::CHECKVAL<int32>(L, 2);

        player->ModifyArenaPoints(amount);
        return 0;
    }

    /**
     * Adds or detracts from the [Player]s current Honor Points
     *
     * @param int32 amount
     */
    int ModifyHonorPoints(lua_State* L, Player* player)
    {
        int32 amount = ALE::CHECKVAL<int32>(L, 2);

        player->ModifyHonorPoints(amount);
        return 0;
    }

    /**
     * Saves the [Player] to the database
     */
    int SaveToDB(lua_State* /*L*/, Player* player)
    {
        player->SaveToDB(false, false);
        return 0;
    }

    /**
     * Sends a summon request to the player from the given summoner
     *
     * @param [Unit] summoner
     */
    int SummonPlayer(lua_State* L, Player* player)
    {
        Unit* summoner = ALE::CHECKOBJ<Unit>(L, 2);

        float x, y, z;
        summoner->GetPosition(x,y,z);
        player->SetSummonPoint(summoner->GetMapId(), x, y, z);

        WorldPacket data(SMSG_SUMMON_REQUEST, 8 + 4 + 4);
        data << summoner->GET_GUID();
        data << uint32(summoner->GetZoneId());
        data << uint32(MAX_PLAYER_SUMMON_DELAY * IN_MILLISECONDS);
        player->GetSession()->SendPacket(&data);
        return 0;
    }

    /**
     * Mutes the [Player] for the amount of seconds specified
     *
     * @param uint32 muteTime
     */
    int Mute(lua_State* L, Player* player)
    {
        uint32 muteseconds = ALE::CHECKVAL<uint32>(L, 2);
        /*const char* reason = luaL_checkstring(E, 2);*/ // Mangos does not have a reason field in database.

        time_t muteTime = GameTime::GetGameTime().count() + muteseconds;
        player->GetSession()->m_muteTime = muteTime;
        LoginDatabase.Execute("UPDATE account SET mutetime = {} WHERE id = {}", muteTime, player->GetSession()->GetAccountId());
        return 0;
    }

    /**
     * Rewards the given quest entry for the [Player] if he has completed it.
     *
     * @param uint32 entry : quest entry
     */
    int RewardQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        Quest const* quest = eObjectMgr->GetQuestTemplate(entry);

        // If player doesn't have the quest
        if (!quest || player->GetQuestStatus(entry) != QUEST_STATUS_COMPLETE)
            return 0;

        player->RewardQuest(quest, 0, player);
        return 0;
    }

    /**
     * Sends an auction house window to the [Player] from the [Unit] specified
     *
     * @param [Unit] sender
     */
    int SendAuctionMenu(lua_State* L, Player* player)
    {
        Unit* unit = ALE::CHECKOBJ<Unit>(L, 2);

        AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(unit->GetFaction());
        if (!ahEntry)
            return 0;

        WorldPacket data(MSG_AUCTION_HELLO, 12);
        data << unit->GET_GUID();
        data << uint32(ahEntry->houseId);
        data << uint8(1);
        player->GetSession()->SendPacket(&data);
        return 0;
    }

    /**
     * Sends a flightmaster window to the [Player] from the [Creature] specified
     *
     * @param [Creature] sender
     */
    int SendTaxiMenu(lua_State* L, Player* player)
    {
        Creature* creature = ALE::CHECKOBJ<Creature>(L, 2);

        player->GetSession()->SendTaxiMenu(creature);
        return 0;
    }

    /**
     * Sends a spirit resurrection request to the [Player]
     */
    int SendSpiritResurrect(lua_State* /*L*/, Player* player)
    {
        player->GetSession()->SendSpiritResurrect();
        return 0;
    }

    /**
     * Sends a tabard vendor window to the [Player] from the [WorldObject] specified
     *
     * @param [WorldObject] sender
     */
    int SendTabardVendorActivate(lua_State* L, Player* player)
    {
        WorldObject* obj = ALE::CHECKOBJ<WorldObject>(L, 2);

        player->GetSession()->SendTabardVendorActivate(obj->GET_GUID());
        return 0;
    }

    /**
     * Sends a bank window to the [Player] from the [WorldObject] specified.
     *
     * @param [WorldObject] sender
     */
    int SendShowBank(lua_State* L, Player* player)
    {
        WorldObject* obj = ALE::CHECKOBJ<WorldObject>(L, 2);

        player->GetSession()->SendShowBank(obj->GET_GUID());
        return 0;
    }
    
    /**
     * Sends a vendor window to the [Player] from the [WorldObject] specified.
     *
     * @param [WorldObject] sender
     */
    int SendListInventory(lua_State* L, Player* player)
    {
        WorldObject* obj = ALE::CHECKOBJ<WorldObject>(L, 2);
        uint32 vendorId = ALE::CHECKVAL<uint32>(L, 3, 0);

        player->GetSession()->SendListInventory(obj->GET_GUID(), vendorId);
        return 0;
    }

    /**
     * Sends a trainer window to the [Player] from the [Creature] specified
     *
     * @param [Creature] sender
     */
    int SendTrainerList(lua_State* L, Player* player)
    {
        Creature* obj = ALE::CHECKOBJ<Creature>(L, 2);

        player->GetSession()->SendTrainerList(obj->GET_GUID());
        return 0;
    }

    /**
     * Sends a guild invitation from the [Player]s [Guild] to the [Player] object specified
     *
     * @param [Player] invitee
     */
    int SendGuildInvite(lua_State* L, Player* player)
    {
        Player* plr = ALE::CHECKOBJ<Player>(L, 2);

        if (Guild* guild = player->GetGuild())
            guild->HandleInviteMember(player->GetSession(), plr->GetName());
        return 0;
    }

    /**
     * Sends an update for the world state to the [Player]
     *
     * @param uint32 field
     * @param uint32 value
     */
    int SendUpdateWorldState(lua_State* L, Player* player)
    {
        uint32 field = ALE::CHECKVAL<uint32>(L, 2);
        uint32 value = ALE::CHECKVAL<uint32>(L, 3);

        player->SendUpdateWorldState(field, value);
        return 0;
    }

    /**
     * Forces the [Player] to log out
     *
     * @param bool saveToDb = true
     */
    int LogoutPlayer(lua_State* L, Player* player)
    {
        bool save = ALE::CHECKVAL<bool>(L, 2, true);

        player->GetSession()->LogoutPlayer(save);
        return 0;
    }

    /**
     * Forcefully removes the [Player] from a [BattleGround] raid group
     */
    int RemoveFromBattlegroundRaid(lua_State* /*L*/, Player* player)
    {
        player->RemoveFromBattlegroundOrBattlefieldRaid();
        return 0;
    }

    /**
     * Unbinds the [Player] from his instances except the one he currently is in.
     *
     * Difficulty is not used on classic.
     *
     * @param uint32 map = true
     * @param uint32 difficulty = 0
     */
    int UnbindInstance(lua_State* L, Player* player)
    {
        uint32 map = ALE::CHECKVAL<uint32>(L, 2);
        uint32 difficulty = ALE::CHECKVAL<uint32>(L, 3, 0);

        if (difficulty < MAX_DIFFICULTY)
            sInstanceSaveMgr->PlayerUnbindInstance(player->GetGUID(), map, Difficulty(difficulty), true, player);
        return 0;
    }

    /**
     * Unbinds the [Player] from his instances except the one he currently is in.
     */
    int UnbindAllInstances(lua_State* /*L*/, Player* player)
    {
        for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
        {
            const BoundInstancesMap& binds = sInstanceSaveMgr->PlayerGetBoundInstances(player->GetGUID(), Difficulty(i));
            for (BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end();)
            {
                if (itr->first != player->GetMapId())
                {
                    sInstanceSaveMgr->PlayerUnbindInstance(player->GetGUID(), itr->first, Difficulty(i), true, player);
                    itr = binds.begin();
                }
                else
                {
                    ++itr;
                }
            }
        }

        return 0;
    }

    /**
     * Forces the [Player] to leave a [BattleGround]
     *
     * @param bool teleToEntry = true
     */
    int LeaveBattleground(lua_State* L, Player* player)
    {
        (void)L; // ensure that the variable is referenced in order to pass compiler checks
        player->LeaveBattleground();
        return 0;
    }

    /**
     * Repairs [Item] at specified position.
     *
     * @param uint16 position
     * @param bool cost = true
     * @param float discountMod = 1.0
     */
    int DurabilityRepair(lua_State* L, Player* player)
    {
        uint16 position = ALE::CHECKVAL<uint16>(L, 2);
        bool takeCost = ALE::CHECKVAL<bool>(L, 3, true);
        float discountMod = ALE::CHECKVAL<float>(L, 4, 1.0f);

        player->DurabilityRepair(position, takeCost, discountMod, false);
        return 0;
    }

    /**
     * Repairs all [Item]s.
     *
     * @param bool takeCost = true
     * @param float discountMod = 1.0
     * @param bool guidBank = false
     */
    int DurabilityRepairAll(lua_State* L, Player* player)
    {
        bool takeCost = ALE::CHECKVAL<bool>(L, 2, true);
        float discountMod = ALE::CHECKVAL<float>(L, 3, 1.0f);
        bool guildBank = ALE::CHECKVAL<bool>(L, 4, false);

        player->DurabilityRepairAll(takeCost, discountMod, guildBank);
        return 0;
    }

    /**
     * Sets durability loss for an [Item] in the specified slot
     *
     * @param int32 slot
     */
    int DurabilityPointLossForEquipSlot(lua_State* L, Player* player)
    {
        int32 slot = ALE::CHECKVAL<int32>(L, 2);

        if (slot >= EQUIPMENT_SLOT_START && slot < EQUIPMENT_SLOT_END)
            player->DurabilityPointLossForEquipSlot((EquipmentSlots)slot);
        return 0;
    }

    /**
     * Sets durability loss on all [Item]s equipped
     *
     * If inventory is true, sets durability loss for [Item]s in bags
     *
     * @param int32 points
     * @param bool inventory = true
     */
    int DurabilityPointsLossAll(lua_State* L, Player* player)
    {
        int32 points = ALE::CHECKVAL<int32>(L, 2);
        bool inventory = ALE::CHECKVAL<bool>(L, 3, true);

        player->DurabilityPointsLossAll(points, inventory);
        return 0;
    }

    /**
     * Sets durability loss for the specified [Item]
     *
     * @param [Item] item
     * @param int32 points
     */
    int DurabilityPointsLoss(lua_State* L, Player* player)
    {
        Item* item = ALE::CHECKOBJ<Item>(L, 2);
        int32 points = ALE::CHECKVAL<int32>(L, 3);

        player->DurabilityPointsLoss(item, points);
        return 0;
    }

    /**
     * Damages specified [Item]
     *
     * @param [Item] item
     * @param double percent
     */
    int DurabilityLoss(lua_State* L, Player* player)
    {
        Item* item = ALE::CHECKOBJ<Item>(L, 2);
        double percent = ALE::CHECKVAL<double>(L, 3);

        player->DurabilityLoss(item, percent);
        return 0;
    }

    /**
     * Damages all [Item]s equipped. If inventory is true, damages [Item]s in bags
     *
     * @param double percent
     * @param bool inventory = true
     */
    int DurabilityLossAll(lua_State* L, Player* player)
    {
        double percent = ALE::CHECKVAL<double>(L, 2);
        bool inventory = ALE::CHECKVAL<bool>(L, 3, true);

        player->DurabilityLossAll(percent, inventory);
        return 0;
    }

    /**
     * Kills the [Player]
     */
    int KillPlayer(lua_State* /*L*/, Player* player)
    {
        player->KillPlayer();
        return 0;
    }

    /**
     * Forces the [Player] to leave a [Group]
     */
    int RemoveFromGroup(lua_State* /*L*/, Player* player)
    {
        if (!player->GetGroup())
            return 0;

        player->RemoveFromGroup();
        return 0;
    }

    /**
     * Returns the [Player]s accumulated talent reset cost
     *
     * @return uint32 resetCost
     */
    int ResetTalentsCost(lua_State* L, Player* player)
    {
        ALE::Push(L, player->resetTalentsCost());
        return 1;
    }

    /**
     * Resets the [Player]s talents
     *
     * @param bool noCost = true
     */
    int ResetTalents(lua_State* L, Player* player)
    {
        bool no_cost = ALE::CHECKVAL<bool>(L, 2, true);

        player->resetTalents(no_cost);
        player->SendTalentsInfoData(false);
        return 0;
    }

    /**
     * Removes the [Spell] from the [Player]
     *
     * @param uint32 entry : entry of a [Spell]
     */
    int RemoveSpell(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        player->removeSpell(entry, SPEC_MASK_ALL, false);
        return 0;
    }

    /**
     * Clears the [Player]s combo points
     */
    int ClearComboPoints(lua_State* /*L*/, Player* player)
    {
        player->ClearComboPoints();
        return 0;
    }

    /**
     * Adds combo points to the [Player]
     *
     * @param [Unit] target
     * @param int8 count
     */
    int AddComboPoints(lua_State* L, Player* player)
    {
        Unit* target = ALE::CHECKOBJ<Unit>(L, 2);
        int8 count = ALE::CHECKVAL<int8>(L, 3);

        player->AddComboPoints(target, count);
        return 0;
    }

    /**
     * Gives [Quest] monster talked to credit
     *
     * @param uint32 entry : entry of a [Creature]
     * @param [Creature] creature
     */
    int TalkedToCreature(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
        Creature* creature = ALE::CHECKOBJ<Creature>(L, 3);

        player->TalkedToCreature(entry, creature->GET_GUID());
        return 0;
    }

    /**
     * Gives [Quest] monster killed credit
     *
     * @param uint32 entry : entry of a [Creature]
     */
    int KilledMonsterCredit(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        player->KilledMonsterCredit(entry, player->GET_GUID());
        return 0;
    }

    /**
     * Completes a [Quest] if in a [Group]
     *
     * @param uint32 quest : entry of a quest
     * @param [WorldObject] obj
     */
    int GroupEventHappens(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        WorldObject* obj = ALE::CHECKOBJ<WorldObject>(L, 3);

        player->GroupEventHappens(questId, obj);
        return 0;
    }

    /**
     * Completes the [Quest] if a [Quest] area is explored, or completes the [Quest]
     *
     * @param uint32 quest : entry of a [Quest]
     */
    int AreaExploredOrEventHappens(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);

        player->AreaExploredOrEventHappens(questId);
        return 0;
    }

    /**
     * Sets the given [Quest] entry failed for the [Player].
     *
     * @param uint32 entry : entry of a [Quest]
     */
    int FailQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        player->FailQuest(entry);
        return 0;
    }

    /**
     * Sets the given quest entry incomplete for the [Player].
     *
     * @param uint32 entry : quest entry
     */
    int IncompleteQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        player->IncompleteQuest(entry);
        return 0;
    }

    /**
     * Completes the given quest entry for the [Player] and tries to satisfy all quest requirements.
     *
     * The player should have the quest to complete it.
     *
     * @param uint32 entry : quest entry
     */
    int CompleteQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        Quest const* quest = eObjectMgr->GetQuestTemplate(entry);

        // If player doesn't have the quest
        if (!quest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE)
            return 0;

        // Add quest items for quests that require items
        for (uint8 x = 0; x < QUEST_ITEM_OBJECTIVES_COUNT; ++x)
        {
            uint32 id = quest->RequiredItemId[x];
            uint32 count = quest->RequiredItemCount[x];

            if (!id || !count)
                continue;

            uint32 curItemCount = player->GetItemCount(id, true);

            ItemPosCountVec dest;
            uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, id, count - curItemCount);
            if (msg == EQUIP_ERR_OK)
            {
                Item* item = player->StoreNewItem(dest, id, true);
                player->SendNewItem(item, count - curItemCount, true, false);
            }
        }

        // All creature/GO slain/cast (not required, but otherwise it will display "Creature slain 0/10")
        for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
        {
            int32 creature = quest->RequiredNpcOrGo[i];
            uint32 creatureCount = quest->RequiredNpcOrGoCount[i];

            if (creature > 0)
            {
                if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creature))
                    for (uint16 z = 0; z < creatureCount; ++z)
                        player->KilledMonster(creatureInfo, ObjectGuid::Empty);
            }
            else if (creature < 0)
                for (uint16 z = 0; z < creatureCount; ++z)
                    player->KillCreditGO(creature);
        }


        // If the quest requires reputation to complete
        if (uint32 repFaction = quest->GetRepObjectiveFaction())
        {
            uint32 repValue = quest->GetRepObjectiveValue();
            uint32 curRep = player->GetReputationMgr().GetReputation(repFaction);
            if (curRep < repValue)
                if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction))
                    player->GetReputationMgr().SetReputation(factionEntry, repValue);
        }

        // If the quest requires a SECOND reputation to complete
        if (uint32 repFaction = quest->GetRepObjectiveFaction2())
        {
            uint32 repValue2 = quest->GetRepObjectiveValue2();
            uint32 curRep = player->GetReputationMgr().GetReputation(repFaction);
            if (curRep < repValue2)
                if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction))
                    player->GetReputationMgr().SetReputation(factionEntry, repValue2);
        }

        // If the quest requires money
        int32 ReqOrRewMoney = quest->GetRewOrReqMoney();
        if (ReqOrRewMoney < 0)
            player->ModifyMoney(-ReqOrRewMoney);

        player->CompleteQuest(entry);
        return 0;
    }

    /**
     * Tries to add the given quest entry for the [Player].
     *
     * @param uint32 entry : quest entry
     */
    int AddQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        Quest const* quest = eObjectMgr->GetQuestTemplate(entry);
        if (!quest)
            return 0;

        // check item starting quest (it can work incorrectly if added without item in inventory)
        ItemTemplateContainer const* itc = sObjectMgr->GetItemTemplateStore();
        ItemTemplateContainer::const_iterator result = find_if(itc->begin(), itc->end(), Finder<uint32, ItemTemplate>(entry, &ItemTemplate::StartQuest));

        if (result != itc->end())
            return 0;

        // ok, normal (creature/GO starting) quest
        if (player->CanAddQuest(quest, true))
            player->AddQuestAndCheckCompletion(quest, NULL);

        return 0;
    }

    /**
     * Removes the given quest entry from the [Player].
     *
     * @param uint32 entry : quest entry
     */
    int RemoveQuest(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

        Quest const* quest = eObjectMgr->GetQuestTemplate(entry);

        if (!quest)
            return 0;

        // remove all quest entries for 'entry' from quest log
        for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
        {
            uint32 logQuest = player->GetQuestSlotQuestId(slot);
            if (logQuest == entry)
            {
                player->SetQuestSlot(slot, 0);

                // we ignore unequippable quest items in this case, its' still be equipped
                player->TakeQuestSourceItem(logQuest, false);

                if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
                {
                    player->pvpInfo.IsHostile = player->pvpInfo.IsInHostileArea || player->HasPvPForcingQuest();
                    player->UpdatePvPState();
                }
            }
        }

        player->RemoveActiveQuest(entry, false);
        player->RemoveRewardedQuest(entry);
        return 0;
    }

    /**
     * Sends whisper text from the [Player]
     *
     * @param string text
     * @param uint32 lang : language the [Player] will speak
     * @param [Player] receiver : is the [Player] that will receive the whisper, if TrinityCore
     * @param ObjectGuid guid : is the GUID of a [Player] that will receive the whisper, not TrinityCore
     */
    int Whisper(lua_State* L, Player* player)
    {
        std::string text = ALE::CHECKVAL<std::string>(L, 2);
        uint32 lang = ALE::CHECKVAL<uint32>(L, 3);
        Player* receiver = ALE::CHECKOBJ<Player>(L, 4);
        player->Whisper(text, (Language)lang, receiver);
        return 0;
    }

    /**
     * Sends a text emote from the [Player]
     *
     * @param string emoteText
     */
    int TextEmote(lua_State* L, Player* player)
    {
        std::string text = ALE::CHECKVAL<std::string>(L, 2);

        player->TextEmote(text);
        return 0;
    }

    /**
     * Sends yell text from the [Player]
     *
     * @param string text : text for the [Player] to yells
     * @param uint32 lang : language the [Player] will speak
     */
    int Yell(lua_State* L, Player* player)
    {
        std::string text = ALE::CHECKVAL<std::string>(L, 2);
        uint32 lang = ALE::CHECKVAL<uint32>(L, 3);
        player->Yell(text, (Language)lang);
        return 0;
    }

    /**
     * Sends say text from the [Player]
     *
     * @param string text : text for the [Player] to say
     * @param uint32 lang : language the [Player] will speak
     */
    int Say(lua_State* L, Player* player)
    {
        std::string text = ALE::CHECKVAL<std::string>(L, 2);
        uint32 lang = ALE::CHECKVAL<uint32>(L, 3);
        player->Say(text, (Language)lang);
        return 0;
    }

    /**
     * Gives the [Player] experience
     *
     * @param uint32 xp : experience to give
     * @param [Unit] victim = nil
     */
    int GiveXP(lua_State* L, Player* player)
    {
        uint32 xp = ALE::CHECKVAL<uint32>(L, 2);
        Unit* victim = ALE::CHECKOBJ<Unit>(L, 3, false);

        player->GiveXP(xp, victim);
        return 0;
    }

    /**
     * Toggle the [Player]s 'Do Not Disturb' flag
     */
    int ToggleDND(lua_State* /*L*/, Player* player)
    {
        player->ToggleDND();
        return 0;
    }

    /**
     * Toggle the [Player]s 'Away From Keyboard' flag
     */
    int ToggleAFK(lua_State* /*L*/, Player* player)
    {
        player->ToggleAFK();
        return 0;
    }

    /**
     * Equips the given item or item entry to the given slot. Returns the equipped item or nil.
     *
     *     enum EquipmentSlots // 19 slots
     *     {
     *         EQUIPMENT_SLOT_START        = 0,
     *         EQUIPMENT_SLOT_HEAD         = 0,
     *         EQUIPMENT_SLOT_NECK         = 1,
     *         EQUIPMENT_SLOT_SHOULDERS    = 2,
     *         EQUIPMENT_SLOT_BODY         = 3,
     *         EQUIPMENT_SLOT_CHEST        = 4,
     *         EQUIPMENT_SLOT_WAIST        = 5,
     *         EQUIPMENT_SLOT_LEGS         = 6,
     *         EQUIPMENT_SLOT_FEET         = 7,
     *         EQUIPMENT_SLOT_WRISTS       = 8,
     *         EQUIPMENT_SLOT_HANDS        = 9,
     *         EQUIPMENT_SLOT_FINGER1      = 10,
     *         EQUIPMENT_SLOT_FINGER2      = 11,
     *         EQUIPMENT_SLOT_TRINKET1     = 12,
     *         EQUIPMENT_SLOT_TRINKET2     = 13,
     *         EQUIPMENT_SLOT_BACK         = 14,
     *         EQUIPMENT_SLOT_MAINHAND     = 15,
     *         EQUIPMENT_SLOT_OFFHAND      = 16,
     *         EQUIPMENT_SLOT_RANGED       = 17,
     *         EQUIPMENT_SLOT_TABARD       = 18,
     *         EQUIPMENT_SLOT_END          = 19
     *     };
     *
     *     enum InventorySlots // 4 slots
     *     {
     *         INVENTORY_SLOT_BAG_START    = 19,
     *         INVENTORY_SLOT_BAG_END      = 23
     *     };
     *
     * @proto equippedItem = (item, slot)
     * @proto equippedItem = (entry, slot)
     * @param [Item] item : item to equip
     * @param uint32 entry : entry of the item to equip
     * @param uint32 slot : equipment slot to equip the item to The slot can be [EquipmentSlots] or [InventorySlots]
     * @return [Item] equippedItem : item or nil if equipping failed
     */
    int EquipItem(lua_State* L, Player* player)
    {
        uint16 dest = 0;
        Item* item = ALE::CHECKOBJ<Item>(L, 2, false);
        uint32 slot = ALE::CHECKVAL<uint32>(L, 3);

        if (slot >= INVENTORY_SLOT_BAG_END)
            return 1;

        if (!item)
        {
            uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
            item = Item::CreateItem(entry, 1, player);
            if (!item)
                return 1;

            InventoryResult result = player->CanEquipItem(slot, dest, item, false);
            if (result != EQUIP_ERR_OK)
            {
                delete item;
                return 1;
            }
            player->ItemAddedQuestCheck(entry, 1);
            player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, entry, 1);
        }
        else
        {
            InventoryResult result = player->CanEquipItem(slot, dest, item, false);
            if (result != EQUIP_ERR_OK)
                return 1;
            player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true);
        }

        ALE::Push(L, player->EquipItem(dest, item, true));
        player->AutoUnequipOffhandIfNeed();
        return 1;
    }
    
    /**
     * Returns true if the player can equip the given [Item] or item entry to the given slot, false otherwise.
     *
     * @proto canEquip = (item, slot)
     * @proto canEquip = (entry, slot)
     * @param [Item] item : item to equip
     * @param uint32 entry : entry of the item to equip
     * @param uint32 slot : equipment slot to test
     * @return bool canEquip
     */
    int CanEquipItem(lua_State* L, Player* player)
    {
        Item* item = ALE::CHECKOBJ<Item>(L, 2, false);
        uint32 slot = ALE::CHECKVAL<uint32>(L, 3);
        if (slot >= EQUIPMENT_SLOT_END)
        {
            ALE::Push(L, false);
            return 1;
        }

        if (!item)
        {
            uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
            uint16 dest;
            InventoryResult msg = player->CanEquipNewItem(slot, dest, entry, false);
            if (msg != EQUIP_ERR_OK)
            {
                ALE::Push(L, false);
                return 1;
            }
        }
        else
        {
            uint16 dest;
            InventoryResult msg = player->CanEquipItem(slot, dest, item, false);
            if (msg != EQUIP_ERR_OK)
            {
                ALE::Push(L, false);
                return 1;
            }
        }
        ALE::Push(L, true);
        return 1;
    }

    /**
     * Removes a title by ID from the [Player]s list of known titles
     *
     * @param uint32 titleId
     */
    int UnsetKnownTitle(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);
        CharTitlesEntry const* t = sCharTitlesStore.LookupEntry(id);
        if (t)
            player->SetTitle(t, true);
        return 0;
    }

    /**
     * Advances all of the [Player]s weapon skills to the maximum amount available
     */
    int AdvanceSkillsToMax(lua_State* /*L*/, Player* player)
    {
        player->UpdateSkillsToMaxSkillsForLevel();
        return 0;
    }

    /**
     * Advances all of the [Player]s skills to the amount specified
     *
     * @param uint32 skillStep
     */
    int AdvanceAllSkills(lua_State* L, Player* player)
    {
        uint32 step = ALE::CHECKVAL<uint32>(L, 2);

        if (!step)
            return 0;

        for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
        {
            if (SkillLineEntry const* entry = sSkillLineStore.LookupEntry(i))
            {
                if (entry->categoryId == SKILL_CATEGORY_LANGUAGES || entry->categoryId == SKILL_CATEGORY_GENERIC)
                    continue;

                if (player->HasSkill(entry->id))
                    player->UpdateSkill(entry->id, step);
            }
        }

        return 0;
    }

    /**
     * Updates a skill for the [Player] and advances it by the specified step.
     *
     * @param uint32 skillId : the skill to update
     * @param uint32 step : the step to advance the skill by
     * @return bool success : true if the skill was updated successfully
     */
    int AdvanceSkill(lua_State* L, Player* player)
    {
        uint32 _skillId = ALE::CHECKVAL<uint32>(L, 2);
        uint32 _step = ALE::CHECKVAL<uint32>(L, 3);
        bool success = false;
        if (_skillId && _step && player->HasSkill(_skillId))
        {
            success = player->UpdateSkill(_skillId, _step);
        }
        ALE::Push(L, success);
        return 1;
    }

    /**
     * Teleports a [Player] to the location specified
     *
     * @param uint32 mappId
     * @param float xCoord
     * @param float yCoord
     * @param float zCoord
     * @param float orientation
     */
    int Teleport(lua_State* L, Player* player)
    {
        uint32 mapId = ALE::CHECKVAL<uint32>(L, 2);
        float x = ALE::CHECKVAL<float>(L, 3);
        float y = ALE::CHECKVAL<float>(L, 4);
        float z = ALE::CHECKVAL<float>(L, 5);
        float o = ALE::CHECKVAL<float>(L, 6);

        if (player->IsInFlight())
        {
            player->GetMotionMaster()->MovementExpired();
            player->m_taxi.ClearTaxiDestinations();
        }

        ALE::Push(L, player->TeleportTo(mapId, x, y, z, o));
        return 1;
    }

    /**
     * Adds a specified number of lifetime honorable kills to the [Player].
     *
     * @param [Player] player
     * @param uint32 kills
     */
    int AddLifetimeKills(lua_State* L, Player* player)
    {
        uint32 val = ALE::CHECKVAL<uint32>(L, 2);
        uint32 currentKills = player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS);
        player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, currentKills + val);
        return 0;
    }
    
    /**
     * Adds the given amount of the specified item entry to the player.
     *
     * @param uint32 entry : entry of the item to add
     * @param uint32 itemCount = 1 : amount of the item to add
     * @return [Item] item : the item that was added or nil
     */
    int AddItem(lua_State* L, Player* player)
    {
        uint32 itemId = ALE::CHECKVAL<uint32>(L, 2);
        uint32 itemCount = ALE::CHECKVAL<uint32>(L, 3, 1);

        uint32 noSpaceForCount = 0;
        ItemPosCountVec dest;
        InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, itemCount, &noSpaceForCount);
        if (msg != EQUIP_ERR_OK)
            itemCount -= noSpaceForCount;

        if (itemCount == 0 || dest.empty())
            return 1;

        Item* item = player->StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
        if (item)
            player->SendNewItem(item, itemCount, true, false);
        ALE::Push(L, item);

        return 1;
    }
    
    /**
     * Removes the given amount of the specified [Item] from the player.
     *
     * @proto (item, itemCount)
     * @proto (entry, itemCount)
     * @param [Item] item : item to remove
     * @param uint32 entry : entry of the item to remove
     * @param uint32 itemCount = 1 : amount of the item to remove
     */
    int RemoveItem(lua_State* L, Player* player)
    {
        Item* item = ALE::CHECKOBJ<Item>(L, 2, false);
        uint32 itemCount = ALE::CHECKVAL<uint32>(L, 3);
        if (!item)
        {
            uint32 itemId = ALE::CHECKVAL<uint32>(L, 2);
            player->DestroyItemCount(itemId, itemCount, true);
        }
        else
        {
            bool all = itemCount >= item->GetCount();
            player->DestroyItemCount(item, itemCount, true);
            if (all)
                ALE::CHECKOBJ<ALEObject>(L, 2)->Invalidate();
        }
        return 0;
    }

    /**
     * Removes specified amount of lifetime kills
     *
     * @param uint32 val : kills to remove
     */
    int RemoveLifetimeKills(lua_State* L, Player* player)
    {
        uint32 val = ALE::CHECKVAL<uint32>(L, 2);
        uint32 currentKills = player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS);
        if (val > currentKills)
            val = currentKills;
        player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, currentKills - val);
        return 0;
    }

    /**
     * Resets cooldown of the specified spell
     *
     * @param uint32 spellId
     * @param bool update = true
     */
    int ResetSpellCooldown(lua_State* L, Player* player)
    {
        uint32 spellId = ALE::CHECKVAL<uint32>(L, 2);
        bool update = ALE::CHECKVAL<bool>(L, 3, true);
        player->RemoveSpellCooldown(spellId, update);
        return 0;
    }

    /**
     * Resets cooldown of the specified category
     *
     * @param uint32 category
     * @param bool update = true
     */
    int ResetTypeCooldowns(lua_State* L, Player* player)
    {
        uint32 category = ALE::CHECKVAL<uint32>(L, 2);
        bool update = ALE::CHECKVAL<bool>(L, 3, true);
        (void)update; // ensure that the variable is referenced in order to pass compiler checks

        player->RemoveCategoryCooldown(category);
        return 0;
    }

    /**
     * Resets all of the [Player]'s cooldowns
     */
    int ResetAllCooldowns(lua_State* /*L*/, Player* player)
    {
        player->RemoveAllSpellCooldown();
        return 0;
    }

    /**
     * Sends a Broadcast Message to the [Player]
     *
     * @param string message
     */
    int SendBroadcastMessage(lua_State* L, Player* player)
    {
        const char* message = ALE::CHECKVAL<const char*>(L, 2);
        if (std::string(message).length() > 0)
            ChatHandler(player->GetSession()).SendSysMessage(message);
        return 0;
    }

    /**
     * Sends an Area Trigger Message to the [Player]
     *
     * @param string message
     */
    int SendAreaTriggerMessage(lua_State* L, Player* player)
    {
        std::string msg = ALE::CHECKVAL<std::string>(L, 2);
        if (msg.length() > 0)
            player->GetSession()->SendAreaTriggerMessage("{}", msg.c_str());
        return 0;
    }

    /**
     * Sends a Notification to the [Player]
     *
     * @param string message
     */
    int SendNotification(lua_State* L, Player* player)
    {
        std::string msg = ALE::CHECKVAL<std::string>(L, 2);
        if (msg.length() > 0)
            ChatHandler(player->GetSession()).SendNotification("{}", msg);
        return 0;
    }

    /**
     * Sends a [WorldPacket] to the [Player]
     *
     * @param [WorldPacket] packet
     * @param bool selfOnly = true
     */
    int SendPacket(lua_State* L, Player* player)
    {
        WorldPacket* data = ALE::CHECKOBJ<WorldPacket>(L, 2);
        bool selfOnly = ALE::CHECKVAL<bool>(L, 3, true);
        if (selfOnly)
            player->GetSession()->SendPacket(data);
        else
            player->SendMessageToSet(data, true);
        return 0;
    }

    /**
     * Sends addon message to the [Player] receiver
     *
     * @param string prefix
     * @param string message
     * @param [ChatMsg] channel
     * @param [Player] receiver
     *
     */
    int SendAddonMessage(lua_State* L, Player* player)
    {
        std::string prefix = ALE::CHECKVAL<std::string>(L, 2);
        std::string message = ALE::CHECKVAL<std::string>(L, 3);
        uint8 channel = ALE::CHECKVAL<uint8>(L, 4);
        Player* receiver = ALE::CHECKOBJ<Player>(L, 5);

        std::string fullmsg = prefix + "\t" + message;

        WorldPacket data(SMSG_MESSAGECHAT, 100);
        data << uint8(channel);
        data << int32(LANG_ADDON);
        data << player->GET_GUID();
        data << uint32(0);
        data << receiver->GET_GUID();
        data << uint32(fullmsg.length() + 1);
        data << fullmsg;
        data << uint8(0);
        receiver->GetSession()->SendPacket(&data);
        return 0;
    }

    /**
     * Kicks the [Player] from the server
     */
    int KickPlayer(lua_State* /*L*/, Player* player)
    {
        player->GetSession()->KickPlayer();
        return 0;
    }

    /**
     * Adds or subtracts from the [Player]s money in copper
     *
     * @param int32 copperAmt : negative to remove, positive to add
     */
    int ModifyMoney(lua_State* L, Player* player)
    {
        int32 amt = ALE::CHECKVAL<int32>(L, 2);

        player->ModifyMoney(amt);
        return 1;
    }

    /**
     * Teaches the [Player] the [Spell] specified by entry ID
     *
     * @param uint32 spellId
     */
    int LearnSpell(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);
        player->learnSpell(id);
        return 0;
    }

    /**
     * Learn the [Player] the talent specified by talent_id and talentRank
     *
     * @param uint32 talent_id
     * @param uint32 talentRank
     */
    int LearnTalent(lua_State* L, Player* player)
    {
        uint32 id = ALE::CHECKVAL<uint32>(L, 2);
        uint32 rank = ALE::CHECKVAL<uint32>(L, 3);

        player->LearnTalent(id, rank);
        player->SendTalentsInfoData(false);

        return 0;
    }
    /**
    * Run a chat command as if the player typed it into the chat
    *
    * @param string command: text to display in chat or console
    */
    int RunCommand(lua_State* L, Player* player)
    {
        auto command = ALE::CHECKVAL<std::string>(L, 2);

        // In _ParseCommands which is used below no leading . or ! is allowed for the command string.
        if (command[0] == '.' || command[0] == '!') {
            command = command.substr(1);
        }

        auto handler = ChatHandler(player->GetSession());
        handler._ParseCommands(command);

        return 0;
    }

    /**
    * Adds a glyph specified by `glyphId` to the [Player]'s current talent specialization into the slot with the index `slotIndex`
    *
    * @param uint32 glyphId
    * @param uint32 slotIndex
    */
    int SetGlyph(lua_State* L, Player* player)
    {
        uint32 glyphId = ALE::CHECKVAL<uint32>(L, 2);
        uint32 slotIndex = ALE::CHECKVAL<uint32>(L, 3);

        player->SetGlyph(slotIndex, glyphId, true);
        player->SendTalentsInfoData(false); // Also handles GlyphData

        return 0;
    }

    /**
    * Returns the glyph ID in the specified glyph slot of the [Player]'s current talent specialization.
    *
    * @param [uint32] slotIndex
    * @return [uint32] glyphId
    */
    int GetGlyph(lua_State* L, Player* player)
    {
        auto slotIndex = ALE::CHECKVAL<uint32>(L, 2);
        ALE::Push(L,player->GetGlyph(slotIndex));
        return 1;
    }

    /**
     * Remove cooldowns on spells that have less than 10 minutes of cooldown from the [Player], similarly to when you enter an arena.
     */
    int RemoveArenaSpellCooldowns(lua_State* /*L*/, Player* player)
    {
        player->RemoveArenaSpellCooldowns();
        return 0;
    }

    /**
     * Resurrects the [Player].
     *
     * @param float healthPercent = 100.0f
     * @param bool ressSickness = false
     */
    int ResurrectPlayer(lua_State* L, Player* player)
    {
        float percent = ALE::CHECKVAL<float>(L, 2, 100.0f);
        bool sickness = ALE::CHECKVAL<bool>(L, 3, false);
        player->ResurrectPlayer(percent, sickness);
        player->SpawnCorpseBones();
        return 0;
    }

    /**
     * Adds a new item to the gossip menu shown to the [Player] on next call to [Player:GossipSendMenu].
     *
     * sender and intid are numbers which are passed directly to the gossip selection handler. Internally they are partly used for the database gossip handling.  
     * code specifies whether to show a box to insert text to. The player inserted text is passed to the gossip selection handler.  
     * money specifies an amount of money the player needs to have to click the option. An error message is shown if the player doesn't have enough money.  
     * Note that the money amount is only checked client side and is not removed from the player either. You will need to check again in your code before taking action.
     *
     * See also: [Player:GossipSendMenu], [Player:GossipAddQuests], [Player:GossipComplete], [Player:GossipClearMenu]
     *
     * @param uint32 icon : number that specifies used icon
     * @param string msg : label on the gossip item
     * @param uint32 sender : number passed to gossip handlers
     * @param uint32 intid : number passed to gossip handlers
     * @param bool code = false : show text input on click if true
     * @param string popup = nil : if non empty string, a popup with given text shown on click
     * @param uint32 money = 0 : required money in copper
     */
    int GossipMenuAddItem(lua_State* L, Player* player)
    {
        uint32 _icon = ALE::CHECKVAL<uint32>(L, 2);
        const char* msg = ALE::CHECKVAL<const char*>(L, 3);
        uint32 _sender = ALE::CHECKVAL<uint32>(L, 4);
        uint32 _intid = ALE::CHECKVAL<uint32>(L, 5);
        bool _code = ALE::CHECKVAL<bool>(L, 6, false);
        const char* _promptMsg = ALE::CHECKVAL<const char*>(L, 7, "");
        uint32 _money = ALE::CHECKVAL<uint32>(L, 8, 0);
        if (player->PlayerTalkClass->GetGossipMenu().GetMenuItemCount() < GOSSIP_MAX_MENU_ITEMS)
        {
            player->PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, _icon, msg, _sender, _intid, _promptMsg, _money,
                                                                 _code);
        }
        else
        {
            return luaL_error(L, "GossipMenuItem not added. Reached Max amount of possible GossipMenuItems in this GossipMenu");
        }
        return 0;
    }

    /**
     * Closes the [Player]s currently open Gossip Menu.
     *
     * See also: [Player:GossipMenuAddItem], [Player:GossipAddQuests], [Player:GossipSendMenu], [Player:GossipClearMenu]
     */
    int GossipComplete(lua_State* /*L*/, Player* player)
    {
        player->PlayerTalkClass->SendCloseGossip();
        return 0;
    }

    /**
     * Sends the current gossip items of the player to him as a gossip menu with header text from the given textId.
     *
     * If sender is a [Player] then menu_id is mandatory, otherwise it is not used for anything.
     * menu_id is the ID used to trigger the OnGossipSelect registered for players. See [Global:RegisterPlayerGossipEvent]
     *
     * See also: [Player:GossipMenuAddItem], [Player:GossipAddQuests], [Player:GossipComplete], [Player:GossipClearMenu]
     *
     * @proto (npc_text, sender)
     * @proto (npc_text, sender, menu_id)
     * @param uint32 npc_text : entry ID of a header text in npc_text database table, common default is 100
     * @param [Object] sender : object acting as the source of the sent gossip menu
     * @param uint32 menu_id : if sender is a [Player] then menu_id is mandatory
     */
    int GossipSendMenu(lua_State* L, Player* player)
    {
        uint32 npc_text = ALE::CHECKVAL<uint32>(L, 2);
        Object* sender = ALE::CHECKOBJ<Object>(L, 3);
        if (sender->GetTypeId() == TYPEID_PLAYER)
        {
            uint32 menu_id = ALE::CHECKVAL<uint32>(L, 4);
            player->PlayerTalkClass->GetGossipMenu().SetMenuId(menu_id);
        }
        player->PlayerTalkClass->SendGossipMenu(npc_text, sender->GET_GUID());
        return 0;
    }

    /**
     * Clears the [Player]s current gossip item list.
     *
     * See also: [Player:GossipMenuAddItem], [Player:GossipSendMenu], [Player:GossipAddQuests], [Player:GossipComplete]
     * 
     *     Note: This is needed when you show a gossip menu without using gossip hello or select hooks which do this automatically.
     *     Usually this is needed when using [Player] is the sender of a Gossip Menu.
     */
    int GossipClearMenu(lua_State* /*L*/, Player* player)
    {
        player->PlayerTalkClass->ClearMenus();
        return 0;
    }

    /**
     * Attempts to start the taxi/flying to the given pathID
     *
     * @param uint32 pathId : pathId from DBC or [Global:AddTaxiPath]
     */
    int StartTaxi(lua_State* L, Player* player)
    {
        uint32 pathId = ALE::CHECKVAL<uint32>(L, 2);

        player->ActivateTaxiPathTo(pathId);
        return 0;
    }

    /**
     * Sends POI to the location on your map
     *
     * @param float x
     * @param float y
     * @param uint32 icon : map icon to show
     * @param uint32 flags
     * @param uint32 data
     * @param string iconText
     */
    int GossipSendPOI(lua_State* L, Player* player)
    {
        float x = ALE::CHECKVAL<float>(L, 2);
        float y = ALE::CHECKVAL<float>(L, 3);
        uint32 icon = ALE::CHECKVAL<uint32>(L, 4);
        uint32 flags = ALE::CHECKVAL<uint32>(L, 5);
        uint32 data = ALE::CHECKVAL<uint32>(L, 6);
        std::string iconText = ALE::CHECKVAL<std::string>(L, 7);

        WorldPacket packet(SMSG_GOSSIP_POI, 4 + 4 + 4 + 4 + 4 + 10);
        packet << flags;
        packet << x;
        packet << y;
        packet << icon;
        packet << data;
        packet << iconText;
        player->GetSession()->SendPacket(&packet);
        return 0;
    }

    /**
     * Adds the gossip items to the [Player]'s gossip for the quests the given [WorldObject] can offer to the player.
     *
     * @param [WorldObject] source : a questgiver with quests
     */
    int GossipAddQuests(lua_State* L, Player* player)
    {
        WorldObject* source = ALE::CHECKOBJ<WorldObject>(L, 2);

        if (source->GetTypeId() == TYPEID_UNIT)
        {
            if (source->GetUInt32Value(UNIT_NPC_FLAGS) & UNIT_NPC_FLAG_QUESTGIVER)
                player->PrepareQuestMenu(source->GET_GUID());
        }
        else if (source->GetTypeId() == TYPEID_GAMEOBJECT)
        {
            if (source->ToGameObject()->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
                player->PrepareQuestMenu(source->GET_GUID());
        }
        return 0;
    }

    /**
     * Shows a quest accepting window to the [Player] for the given quest.
     *
     * @param uint32 questId : entry of a quest
     * @param bool activateAccept = true : auto finish the quest
     */
    int SendQuestTemplate(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        bool activateAccept = ALE::CHECKVAL<bool>(L, 3, true);

        Quest const* quest = eObjectMgr->GetQuestTemplate(questId);
        if (!quest)
            return 0;

        player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GET_GUID(), activateAccept);
        return 0;
    }

    /**
     * Converts [Player]'s corpse to bones
     */
    int SpawnBones(lua_State* /*L*/, Player* player)
    {
        player->SpawnCorpseBones();
        return 0;
    }

    /**
     * Loots [Player]'s bones for insignia
     *
     * @param [Player] looter
     */
    int RemovedInsignia(lua_State* L, Player* player)
    {
        Player* looter = ALE::CHECKOBJ<Player>(L, 2);
        player->RemovedInsignia(looter);
        return 0;
    }

    /**
     * Makes the [Player] invite another player to a group.
     *
     * @param [Player] invited : player to invite to group
     * @return bool success : true if the player was invited to a group
     */
    int GroupInvite(lua_State* L, Player* player)
    {
        Player* invited = ALE::CHECKOBJ<Player>(L, 2);

        if (invited->GetGroup() || invited->GetGroupInvite())
        {
            ALE::Push(L, false);
            return 1;
        }

        // Get correct existing group if any
        Group* group = player->GetGroup();
        if (group && group->isBGGroup())
            group = player->GetOriginalGroup();

        bool success = false;

        // Try invite if group found
        if (group)
            success = !group->IsFull() && group->AddInvite(invited);
        else
        {
            // Create new group if one not found
            group = new Group;
            success = group->AddLeaderInvite(player) && group->AddInvite(invited);
            if (!success)
                delete group;
        }

        if (success)
        {
            WorldPacket data(SMSG_GROUP_INVITE, 10);                // guess size
            data << uint8(1);                                       // invited/already in group flag
            data << player->GetName();                              // max len 48
            data << uint32(0);                                      // unk
            data << uint8(0);                                       // count
            data << uint32(0);                                      // unk
            invited->GetSession()->SendPacket(&data);
        }

        ALE::Push(L, success);
        return 1;
    }

    /**
     * Creates a new [Group] with the creator [Player] as leader.
     *
     * @param [Player] invited : player to add to group
     * @return [Group] createdGroup : the created group or nil
     */
    int GroupCreate(lua_State* L, Player* player)
    {
        Player* invited = ALE::CHECKOBJ<Player>(L, 2);

        if (player->GetGroup() || invited->GetGroup())
            return 0;

        if (player->GetGroupInvite())
            player->UninviteFromGroup();
        if (invited->GetGroupInvite())
            invited->UninviteFromGroup();

        // Try create new group
        Group* group = new Group;
        if (!group->AddLeaderInvite(player))
        {
            delete group;
            return 0;
        }

        // Forming a new group, create it
        if (!group->IsCreated())
        {
            group->RemoveInvite(player);
            group->Create(player);
            sGroupMgr->AddGroup(group);
        }

        if (!group->AddMember(invited))
            return 0;
        group->BroadcastGroupUpdate();
        ALE::Push(L, group);
        return 1;
    }

    /**
     * Starts a cinematic for the [Player]
     *
     * @param uint32 CinematicSequenceId : entry of a cinematic
     */
    int SendCinematicStart(lua_State* L, Player* player)
    {
        uint32 CinematicSequenceId = ALE::CHECKVAL<uint32>(L, 2);

        player->SendCinematicStart(CinematicSequenceId);
        return 0;
    }

    /**
     * Starts a movie for the [Player]
     *
     * @param uint32 MovieId : entry of a movie
     */
    int SendMovieStart(lua_State* L, Player* player)
    {
        uint32 MovieId = ALE::CHECKVAL<uint32>(L, 2);

        player->SendMovieStart(MovieId);
        return 0;
    }

    /**
     * Sets a setting value for the [Player]
     *
     * @param string source
     * @param uint32 index
     * @param uint32 value
     */
    int UpdatePlayerSetting(lua_State* L, Player* player)
    {
        std::string source = ALE::CHECKVAL<std::string>(L, 2);
        uint32 index = ALE::CHECKVAL<uint32>(L, 3);
        uint32 value = ALE::CHECKVAL<uint32>(L, 4);
        player->UpdatePlayerSetting(source, index, value);
        return 0;
    }

    /**
     * Gets a setting value for the [Player]
     *
     * @param string source
     * @param uint32 index
     */
    int GetPlayerSettingValue(lua_State* L, Player* player)
    {
        std::string source = ALE::CHECKVAL<std::string>(L, 2);
        uint32 index = ALE::CHECKVAL<uint32>(L, 3);
        uint32 value = player->GetPlayerSetting(source, index).value;
        ALE::Push(L, value);
        return 1;
    }

    /**
     * Returns the [Player] that is currently trading with this [Player]
     *
     * @return [Player] trader : the player trading, or nil
     */
    int GetTrader(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetTrader());
        return 1;
    }

    /**
     * The [Player] sets the spell power
     *
     * @param int value : The spell power value to set
     * @param bool apply = false : Whether the spell power should be applied or removed
     */
    int SetSpellPower(lua_State* L, Player* player)
    {
        int value  = ALE::CHECKVAL<int>(L, 2);
        bool apply = ALE::CHECKVAL<bool>(L, 3, false);

        player->ApplySpellPowerBonus(value, apply);
        return 0;
    }

    /*int BindToInstance(lua_State* L, Player* player)
    {
    player->BindToInstance();
    return 0;
    }*/

    /*int AddTalent(lua_State* L, Player* player)
    {
    uint32 spellId = ALE::CHECKVAL<uint32>(L, 2);
    uint8 spec = ALE::CHECKVAL<uint8>(L, 3);
    bool learning = ALE::CHECKVAL<bool>(L, 4, true);
    if (spec >= MAX_TALENT_SPECS)
    ALE::Push(L, false);
    else
    ALE::Push(L, player->AddTalent(spellId, spec, learning));
    return 1;
    }*/

    /*int GainSpellComboPoints(lua_State* L, Player* player)
    {
    int8 count = ALE::CHECKVAL<int8>(L, 2);

    player->GainSpellComboPoints(count);
    return 0;
    }*/

    /*int KillGOCredit(lua_State* L, Player* player)
    {
    uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
    ObjectGuid guid = ALE::CHECKVAL<ObjectGuid>(L, 3);
    player->KillCreditGO(entry, guid);
    return 0;
    }*/

    /*int KilledPlayerCredit(lua_State* L, Player* player)
    {
    player->KilledPlayerCredit();
    return 0;
    }*/

    /*int RemoveRewardedQuest(lua_State* L, Player* player)
    {
    uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

    player->RemoveRewardedQuest(entry);
    return 0;
    }*/

    /*int RemoveActiveQuest(lua_State* L, Player* player)
    {
    uint32 entry = ALE::CHECKVAL<uint32>(L, 2);

    player->RemoveActiveQuest(entry);
    return 0;
    }*/

    /*int SummonPet(lua_State* L, Player* player)
    {
    uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
    float x = ALE::CHECKVAL<float>(L, 3);
    float y = ALE::CHECKVAL<float>(L, 4);
    float z = ALE::CHECKVAL<float>(L, 5);
    float o = ALE::CHECKVAL<float>(L, 6);
    uint32 petType = ALE::CHECKVAL<uint32>(L, 7);
    uint32 despwtime = ALE::CHECKVAL<uint32>(L, 8);

    if (petType >= MAX_PET_TYPE)
    return 0;

    player->SummonPet(entry, x, y, z, o, (PetType)petType, despwtime);
    return 0;
    }*/

    /*int RemovePet(lua_State* L, Player* player)
    {
    int mode = ALE::CHECKVAL<int>(L, 2, PET_SAVE_AS_DELETED);
    bool returnreagent = ALE::CHECKVAL<bool>(L, 2, false);

    if (!player->GetPet())
    return 0;

    player->RemovePet(player->GetPet(), (PetSaveMode)mode, returnreagent);
    return 0;
    }*/

    /**
     * Set bonus talent count to a specific count for the [Player]
     *
     * @param uint32 value : bonus talent points
     */
    int SetBonusTalentCount(lua_State* L, Player* player)
    {
        uint32 value = ALE::CHECKVAL<uint32>(L, 2);

        player->SetBonusTalentCount(value);
        return 0;
    }

    /**
     * Get bonus talents count from the [Player]
     *
     * @return uint32 bonusTalent
     */
    int GetBonusTalentCount(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetBonusTalentCount());
        return 1;
    }
  
    /**
     *  Returns the [Player] spells list
     *
     * @return table playerSpells
     */
    int GetSpells(lua_State* L, Player* player)
    {
        std::list<uint32> list;
        lua_createtable(L, list.size(), 0);
        int tbl = lua_gettop(L);
        uint32 i = 0;

        PlayerSpellMap spellMap = player->GetSpellMap();
        for (PlayerSpellMap::const_iterator itr = spellMap.begin(); itr != spellMap.end(); ++itr)
        {
            SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
            ALE::Push(L, spellInfo->Id);
            lua_rawseti(L, tbl, ++i);
        }

        lua_settop(L, tbl);
        return 1;
    }

    /**
     * Add bonus talents count to the [Player]
     *
     * @param uint32 count = count of bonus talent
     */
    int AddBonusTalent(lua_State* L, Player* player)
    {
        uint32 count = ALE::CHECKVAL<uint32>(L, 2);

        player->AddBonusTalent(count);
        return 0;
    }

    /**
     * Remove bonus talents count to the [Player]
     *
     * @param uint32 count = count of bonus talent
     */
    int RemoveBonusTalent(lua_State* L, Player* player)
    {
        uint32 count = ALE::CHECKVAL<uint32>(L, 2);

        player->RemoveBonusTalent(count);
        return 0;
    }
  
    /**
     *  Returns the [Player] homebind location.
     *
     *  @return table homebind : a table containing the player's homebind information:
     *      - uint32 mapId: The ID of the map where the player is bound.
     *      - float x: The X coordinate of the homebind location.
     *      - float y: The Y coordinate of the homebind location.
     *      - float z: The Z coordinate of the homebind location.
     */
    int GetHomebind(lua_State* L, Player* player)
    {
        lua_newtable(L);
        lua_pushinteger(L, player->m_homebindMapId);
        lua_setfield(L, -2, "mapId");

        lua_pushnumber(L, player->m_homebindX);
        lua_setfield(L, -2, "x");

        lua_pushnumber(L, player->m_homebindY);
        lua_setfield(L, -2, "y");

        lua_pushnumber(L, player->m_homebindZ);
        lua_setfield(L, -2, "z");

        return 1;
    }

    /**
     *  Teleports [Player] to a predefined location based on the teleport name.
     *
     *  @param string tele : The name of the predefined teleport location.
     */
    int TeleportTo(lua_State* L, Player* player)
    {
        std::string tele = ALE::CHECKVAL<std::string>(L, 2);
        const GameTele* game_tele = sObjectMgr->GetGameTele(tele);

        if (player->IsInFlight())
        {
            player->GetMotionMaster()->MovementExpired();
            player->m_taxi.ClearTaxiDestinations();
        }

        player->TeleportTo(game_tele->mapId, game_tele->position_x, game_tele->position_y, game_tele->position_z, game_tele->orientation);
        return 0;
    }

    /**
     * Returns the [Player]'s current [Pet], if any.
     *
     * @return [Pet] pet : the player's pet, or `nil` if no pet
     */
    int GetPet(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetPet());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is at maximum level, `false` otherwise.
     *
     * @return bool isMaxLevel
     */
    int IsMaxLevel(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsMaxLevel());
        return 1;
    }

    /**
     * Summons a [Pet] at the specified location.
     *
     * @param uint32 entry : the creature entry ID to summon
     * @param float x : X coordinate
     * @param float y : Y coordinate
     * @param float z : Z coordinate
     * @param float ang : orientation angle
     * @param [PetType] petType : the type of pet to summon
     * @param uint32 duration = 0 : duration in milliseconds, 0 for permanent
     * @param uint32 healthPct = 0 : initial health percentage
     * @return [Pet] pet : the summoned pet, or `nil` if failed
     */
    int SummonPet(lua_State* L, Player* player)
    {
        uint32 entry = ALE::CHECKVAL<uint32>(L, 2);
        float x = ALE::CHECKVAL<float>(L, 3);
        float y = ALE::CHECKVAL<float>(L, 4);
        float z = ALE::CHECKVAL<float>(L, 5);
        float ang = ALE::CHECKVAL<float>(L, 6);
        uint32 petType = ALE::CHECKVAL<uint32>(L, 7);
        uint32 duration = ALE::CHECKVAL<uint32>(L, 8, 0);
        uint32 healthPct = ALE::CHECKVAL<uint32>(L, 9, 0);

        Pet* pet = player->SummonPet(entry, x, y, z, ang, static_cast<PetType>(petType), Milliseconds(duration), healthPct);
        ALE::Push(L, pet);
        return 1;
    }

    /**
     * Returns the average item level of the [Player]'s equipment.
     *
     * @return float averageItemLevel
     */
    int GetAverageItemLevel(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetAverageItemLevel());
        return 1;
    }

    /**
     * Creates a tamed [Pet] from a [Creature] or creature entry.
     *
     * Can be called with either:
     * - `player:CreatePet(creatureEntry)` - creates pet from entry ID
     * - `player:CreatePet(creature, spellID)` - tames existing creature
     *
     * @param uint32 creatureEntry : creature entry ID (first form)
     * @param [Creature] creature : target creature to tame (second form)
     * @param uint32 spellID = 0 : spell used for taming (second form)
     * @return [Pet] pet : the created pet, or `nil` if failed
     */
    int CreatePet(lua_State* L, Player* player)
    {
        if (lua_gettop(L) == 2)
        {
            uint32 creatureEntry = ALE::CHECKVAL<uint32>(L, 2);
            Pet* pet = player->CreatePet(creatureEntry);
            ALE::Push(L, pet);
        }
        else
        {
            Creature* creatureTarget = ALE::CHECKOBJ<Creature>(L, 2);
            uint32 spellID = ALE::CHECKVAL<uint32>(L, 3, 0);
            Pet* pet = player->CreatePet(creatureTarget, spellID);
            ALE::Push(L, pet);
        }
        return 1;
    }

    /**
     * Returns `true` if the [Player] has completed the daily quest, `false` otherwise.
     *
     * @param uint32 questId
     * @return bool isDailyQuestDone
     */
    int IsDailyQuestDone(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        ALE::Push(L, player->IsDailyQuestDone(questId));
        return 1;
    }

    /**
     * Temporarily unsummons the [Player]'s current [Pet].
     *
     * The pet can be resummoned later. Used during teleportation, mounting, etc.
     */
    int UnsummonPetTemporarily(lua_State* /*L*/, Player* player)
    {
        player->UnsummonPetTemporaryIfAny();
        return 0;
    }

    /**
     * Sets the specified player flag on the [Player].
     *
     * @param uint32 flag : the player flag to set
     */
    int SetPlayerFlag(lua_State* L, Player* player)
    {
        uint32 flag = ALE::CHECKVAL<uint32>(L, 2);
        player->SetPlayerFlag((PlayerFlags)flag);
        return 0;
    }

    /**
     * Removes the specified [Pet] from the [Player].
     *
     * @param [Pet] pet : the pet to remove
     * @param [PetSaveMode] mode : how to handle pet removal
     * @param bool returnReagent = false : if `true`, returns reagents used to summon
     */
    int RemovePet(lua_State* L, Player* player)
    {
        Pet* pet = ALE::CHECKOBJ<Pet>(L, 2);
        uint32 mode = ALE::CHECKVAL<uint32>(L, 3);
        bool returnReagent = ALE::CHECKVAL<bool>(L, 4, false);
        player->RemovePet(pet, static_cast<PetSaveMode>(mode), returnReagent);
        return 1;
    }

    /**
     * Removes the specified player flag from the [Player].
     *
     * @param uint32 flag : the player flag to remove
     */
    int RemovePlayerFlag(lua_State* L, Player* player)
    {
        uint32 flag = ALE::CHECKVAL<uint32>(L, 2);
        player->RemovePlayerFlag((PlayerFlags)flag);
        return 0;
    }

    /**
     * Returns `true` if the [Player] can resurrect their [Pet] and returns `false` otherwise.
     *
     * @return bool canResurrect
     */
    int CanPetResurrect(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanPetResurrect());
        return 1;
    }

    /**
     * Returns a random number between the specified minimum and maximum values.
     *
     * @param uint32 minimum : the minimum value
     * @param uint32 maximum : the maximum value
     * @return uint32 randomValue : a random number between min and max
     */
    int DoRandomRoll(lua_State* L, Player* player)
    {
        uint32 minimum = ALE::CHECKVAL<uint32>(L, 2);
        uint32 maximum = ALE::CHECKVAL<uint32>(L, 3);
        ALE::Push(L, player->DoRandomRoll(minimum, maximum));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is flagged for PvP, `false` otherwise.
     *
     * @return bool isPvP
     */
    int IsPvP(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsPvP());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is flagged for Free-for-all PvP, `false` otherwise.
     *
     * @return bool isFFAPvP
     */
    int IsFFAPvP(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsFFAPvP());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is using the Looking for Group system, `false` otherwise.
     *
     * @return bool isUsingLfg
     */
    int IsUsingLfg(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsUsingLfg());
        return 1;
    }

    /**
     * Returns `true` if the [Player] is in a random LFG dungeon, `false` otherwise.
     *
     * @return bool inRandomLfgDungeon
     */
    int InRandomLfgDungeon(lua_State* L, Player* player)
    {
        ALE::Push(L, player->inRandomLfgDungeon());
        return 1;
    }

    /**
     * Returns `true` if the [Player] can interact with the specified quest giver, `false` otherwise.
     *
     * @param [Object] questGiver : the quest giver object
     * @return bool canInteract
     */
    int CanInteractWithQuestGiver(lua_State* L, Player* player)
    {
        Object* questGiver = ALE::CHECKOBJ<Object>(L, 2);
        ALE::Push(L, player->CanInteractWithQuestGiver(questGiver));
        return 1;
    }

    /**
     * Returns `true` if the [Player] can see the specified quest start, `false` otherwise.
     *
     * @param [Quest] quest : the quest to check
     * @return bool canSeeStartQuest
     */
    int CanSeeStartQuest(lua_State* L, Player* player)
    {
        Quest const* quest = ALE::CHECKOBJ<Quest>(L, 2);
        ALE::Push(L, player->CanSeeStartQuest(quest));
        return 1;
    }

    /**
     * Returns `true` if the [Player] has a [Pet] (active or stored) and returns `false` otherwise.
     *
     * @return bool hasExistingPet
     */
    int IsExistPet(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsExistPet());
        return 1;
    }

    /**
     * Returns `true` if the [Player] can take the specified quest, `false` otherwise.
     *
     * @param [Quest] quest : the quest to check
     * @param bool msg : whether to send error messages
     * @return bool canTakeQuest
     */
    int CanTakeQuest(lua_State* L, Player* player)
    {
        Quest const* quest = ALE::CHECKOBJ<Quest>(L, 2);
        bool msg = ALE::CHECKVAL<bool>(L, 3, true);
        ALE::Push(L, player->CanTakeQuest(quest, msg));
        return 1;
    }

    /**
     * Resets the [Player]'s pet talents.
     *
     */
    int ResetPetTalents(lua_State* /*L*/, Player* player)
    {
        player->ResetPetTalents();
        return 0;
    }

    /**
     * Returns `true` if the [Player] can add the specified quest, `false` otherwise.
     *
     * @param [Quest] quest : the quest to check
     * @param bool msg : whether to send error messages
     * @return bool canAddQuest
     */
    int CanAddQuest(lua_State* L, Player* player)
    {
        Quest const* quest = ALE::CHECKOBJ<Quest>(L, 2);
        bool msg = ALE::CHECKVAL<bool>(L, 3, true);
        ALE::Push(L, player->CanAddQuest(quest, msg));
        return 1;
    }

    /**
     * Returns the barber shop cost for the specified style changes.
     *
     * @param uint8 newhairstyle : the new hair style
     * @param uint8 newhaircolor : the new hair color
     * @param uint8 newfacialhair : the new facial hair
     * @return uint32 cost : the cost in copper
     */
    int GetBarberShopCost(lua_State* L, Player* player)
    {
        uint8 newhairstyle = ALE::CHECKVAL<uint8>(L, 2);
        uint8 newhaircolor = ALE::CHECKVAL<uint8>(L, 3);
        uint8 newfacialhair = ALE::CHECKVAL<uint8>(L, 4);
        ALE::Push(L, player->GetBarberShopCost(newhairstyle, newhaircolor, newfacialhair));
        return 1;
    }

    /**
     * Returns the sight range of the [Player] for the specified target.
     *
     * @param [WorldObject] target : the target to check sight range for (optional)
     * @return float sightRange
     */
    int GetSightRange(lua_State* L, Player* player)
    {
        WorldObject* target = ALE::CHECKOBJ<WorldObject>(L, 2, false);
        ALE::Push(L, player->GetSightRange(target));
        return 1;
    }

    /**
     * Calculates reputation gain for the [Player].
     *
     * @param uint32 source : reputation source
     * @param uint32 creatureOrQuestLevel : creature or quest level
     * @param float rep : base reputation
     * @param uint32 faction : faction ID
     * @param bool noQuestBonus : whether to skip quest bonus
     * @return float reputationGain
     */
    int CalculateReputationGain(lua_State* L, Player* player)
    {
        uint32 source = ALE::CHECKVAL<uint32>(L, 2);
        uint32 creatureOrQuestLevel = ALE::CHECKVAL<uint32>(L, 3);
        float rep = ALE::CHECKVAL<float>(L, 4);
        uint32 faction = ALE::CHECKVAL<uint32>(L, 5);
        bool noQuestBonus = ALE::CHECKVAL<bool>(L, 6, false);
        
        ALE::Push(L, player->CalculateReputationGain((ReputationSource)source, creatureOrQuestLevel, rep, faction, noQuestBonus));
        return 1;
    }

    /**
     * Applies environmental damage to the [Player].
     *
     * @param uint32 type : environmental damage type
     * @param uint32 damage : damage amount
     * @return uint32 actualDamage : the actual damage dealt
     */
    int EnvironmentalDamage(lua_State* L, Player* player)
    {
        uint32 type = ALE::CHECKVAL<uint32>(L, 2);
        uint32 damage = ALE::CHECKVAL<uint32>(L, 3);
        ALE::Push(L, player->EnvironmentalDamage((EnviromentalDamage)type, damage));
        return 1;
    }

    /**
     * Initializes taxi nodes for the [Player]'s current level.
     */
    int InitTaxiNodesForLevel(lua_State* /*L*/, Player* player)
    {
        player->InitTaxiNodesForLevel();
        return 0;
    }

    /**
     * Learns a pet talent for the specified [Pet] of the [Player].
     *
     * @param ObjectGuid petGuid : GUID of the pet to learn the talent for
     * @param uint32 talentId : ID of the talent to learn
     * @param uint32 talentRank : rank of the talent to learn
     */
    int LearnPetTalent(lua_State* L, Player* player)
    {
        ObjectGuid petGuid = ALE::CHECKVAL<ObjectGuid>(L, 2);
        uint32 talentId = ALE::CHECKVAL<uint32>(L, 3);
        uint32 talentRank = ALE::CHECKVAL<uint32>(L, 4);
        player->LearnPetTalent(petGuid, talentId, talentRank);
        return 0;
    }

    /**
     * Returns `true` if the [Player] has a title by bit index, `false` otherwise.
     *
     * @param uint32 bitIndex : the title bit index to check
     * @return bool hasTitle
     */
    int HasTitleByIndex(lua_State* L, Player* player)
    {
        uint32 bitIndex = ALE::CHECKVAL<uint32>(L, 2);
        ALE::Push(L, player->HasTitle(bitIndex));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is at group reward distance from the target, `false` otherwise.
     *
     * @param [WorldObject] target : the target to check distance to
     * @return bool isAtGroupRewardDistance
     */
    int IsAtGroupRewardDistance(lua_State* L, Player* player)
    {
        WorldObject const* target = ALE::CHECKOBJ<WorldObject>(L, 2);
        ALE::Push(L, player->IsAtGroupRewardDistance(target));
        return 1;
    }

    /**
     * Returns `true` if the [Player] is at loot reward distance from the target, `false` otherwise.
     *
     * @param [WorldObject] target : the target to check distance to
     * @return bool isAtLootRewardDistance
     */
    int IsAtLootRewardDistance(lua_State* L, Player* player)
    {
        WorldObject const* target = ALE::CHECKOBJ<WorldObject>(L, 2);
        ALE::Push(L, player->IsAtLootRewardDistance(target));
        return 1;
    }

    /**
     * Abandons a quest from the [Player]'s quest log.
     *
     * @param uint32 questId : the quest entry ID to abandon
     */
    int AbandonQuest(lua_State* L, Player* player)
    {
        uint32 questId = ALE::CHECKVAL<uint32>(L, 2);
        player->AbandonQuest(questId);
        return 0;
    }

    /**
     * Returns `true` if the [Player] can tame exotic pets, and `false` otherwise.
     *
     * @return bool canTameExoticPets : `true` if the player can tame exotic pets, `false` otherwise
     */
    int CanTameExoticPets(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanTameExoticPets());
        return 1;
    }

    /**
     * Returns the [Player]'s weapon proficiency flags.
     *
     * @return uint32 proficiencyFlags : bitmask of weapon proficiencies
     */
    int GetWeaponProficiency(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetWeaponProficiency());
        return 1;
    }

    /**
     * Returns the temporary unsummoned pet number for the [Player].
     *
     * @return uint32 petNumber : the temporary unsummoned pet number
     */
    int GetTemporaryUnsummonedPetNumber(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetTemporaryUnsummonedPetNumber());
        return 1;
    }

    /**
     * Returns the [Player]'s armor proficiency flags.
     *
     * @return uint32 proficiencyFlags : bitmask of armor proficiencies
     */
    int GetArmorProficiency(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetArmorProficiency());
        return 1;
    }

    /**
     * Sets the temporary unsummoned pet number for the [Player].
     *
     * @param uint32 petNumber : the pet number to set
     */
    int SetTemporaryUnsummonedPetNumber(lua_State* L, Player* player)
    {
        uint32 petNumber = ALE::CHECKVAL<uint32>(L, 2);
        player->SetTemporaryUnsummonedPetNumber(petNumber);
        return 0;
    }

    /**
     * Adds weapon proficiency to the [Player].
     *
     * @param uint32 flag : weapon proficiency flag to add
     */
    int AddWeaponProficiency(lua_State* L, Player* player)
    {
        uint32 flag = ALE::CHECKVAL<uint32>(L, 2);
        player->AddWeaponProficiency(flag);
        return 0;
    }

    /**
     * Resummons the [Player]'s pet if it was temporarily unsummoned.
     *
     */
    int ResummonPetTemporaryUnSummonedIfAny(lua_State* /*L*/, Player* player)
    {
        player->ResummonPetTemporaryUnSummonedIfAny();
        return 0;
    }

    /**
     * Adds armor proficiency to the [Player].
     *
     * @param uint32 flag : armor proficiency flag to add
     */
    int AddArmorProficiency(lua_State* L, Player* player)
    {
        uint32 flag = ALE::CHECKVAL<uint32>(L, 2);
        player->AddArmorProficiency(flag);
        return 0;
    }

    /**
     * Returns `true` if the [Player] needs to temporarily unsummon their [Pet], and `false` otherwise.
     *
     *
     * @return bool isPetNeedBeTemporaryUnsummoned : `true` if the pet needs to be temporarily unsummoned, `false` otherwise
     */
    int IsPetNeedBeTemporaryUnsummoned(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsPetNeedBeTemporaryUnsummoned());
        return 1;
    }

    /**
     * Sets the [Player]'s ammo item.
     *
     * @param uint32 itemEntry : ammo item entry ID
     */
    int SetAmmo(lua_State* L, Player* player)
    {
        uint32 itemEntry = ALE::CHECKVAL<uint32>(L, 2);
        player->SetAmmo(itemEntry);
        return 0;
    }

    /**
     * Removes the [Player]'s ammo.
     */
    int RemoveAmmo(lua_State* /*L*/, Player* player)
    {
        player->RemoveAmmo();
        return 0;
    }

    /**
     * Returns the [Player]'s ammo DPS.
     *
     * @return float ammoDPS : damage per second from ammo
     */
    int GetAmmoDPS(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetAmmoDPS());
        return 1;
    }

    /**
     * Returns `true` if the [Player] can resummon a [Pet] with the specified spell ID, and `false` otherwise.
     *
     * @param uint32 spellId : the spell ID to check
     * @return bool canResummon : `true` if the player can resummon the pet, `false` otherwise
     */
    int CanResummonPet(lua_State* L, Player* player)
    {
        uint32 spellId = ALE::CHECKVAL<uint32>(L, 2);
        ALE::Push(L, player->CanResummonPet(spellId));
        return 1;
    }

    /**
     * Returns the [Player]'s shield item.
     *
     * @return [Item] shield : the equipped shield or nil
     */
    int GetShield(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetShield());
        return 1;
    }

    /**
     * Returns the last pet number for the [Player].
     *
     * @return uint32 petNumber : the last pet number
     */
    int GetLastPetNumber(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetLastPetNumber());
        return 1;
    }

    /**
     * Returns `true` if the [Player] can teleport, `false` otherwise.
     *
     * @return bool canTeleport
     */
    int CanTeleport(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanTeleport());
        return 1;
    }

    /**
     * Sets the last pet number for the [Player].
     *
     * @param uint32 petNumber : the pet number to set
     */
    int SetLastPetNumber(lua_State* L, Player* player)
    {
        uint32 petNumber = ALE::CHECKVAL<uint32>(L, 2);
        player->SetLastPetNumber(petNumber);
        return 0;
    }

    /**
     * Sets whether the [Player] can teleport.
     *
     * @param bool canTeleport : true to allow teleportation, false to disallow
     */
    int SetCanTeleport(lua_State* L, Player* player)
    {
        bool canTeleport = ALE::CHECKVAL<bool>(L, 2);
        player->SetCanTeleport(canTeleport);
        return 0;
    }

    /**
     * Returns the spell ID of the [Player]'s last [Pet] summoning spell.
     *
     * @return uint32 petSpell : the pet spell ID
     */
    int GetLastPetSpell(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetLastPetSpell());
        return 1;
    }

    /**
     * Returns the [Player]'s runes state for Death Knights.
     *
     * @return uint32 runesState : current runes state bitmask
     */
    int GetRunesState(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetRunesState());
        return 1;
    }

    /**
     * Sets the spell ID of the [Player]'s last [Pet] summoning spell.
     *
     * @param uint32 petSpell : the pet spell ID to set
     */
    int SetLastPetSpell(lua_State* L, Player* player)
    {
        uint32 petSpell = ALE::CHECKVAL<uint32>(L, 2);
        player->SetLastPetSpell(petSpell);
        return 0;
    }

    /**
     * Returns `true` if the [Player] is a spectator, `false` otherwise.
     *
     * @return bool isSpectator
     */
    int IsSpectator(lua_State* L, Player* player)
    {
        ALE::Push(L, player->IsSpectator());
        return 1;
    }

    /**
     * Sets the [Player] as spectator.
     *
     * @param bool isSpectator : true to set as spectator, false otherwise
     */
    int SetIsSpectator(lua_State* L, Player* player)
    {
        bool isSpectator = ALE::CHECKVAL<bool>(L, 2);
        player->SetIsSpectator(isSpectator);
        return 0;
    }

    /**
     * Returns `true` if the [Player] can see Death Knight [Pet]s, and `false` otherwise.
     *
     * @return bool canSeeDKPet
     */
    int CanSeeDKPet(lua_State* L, Player* player)
    {
        ALE::Push(L, player->CanSeeDKPet());
        return 1;
    }

    /**
     * Returns the [Player]'s current viewpoint target.
     *
     * @return [WorldObject] viewpoint : the object the player is viewing from
     */
    int GetViewpoint(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetViewpoint());
        return 1;
    }

    /**
     * Sets whether the [Player] can see Death  Knight [Pet]s.
     *
     * @param bool show : `true` to show DK pets, `false` to hide them
     */
    int SetShowDKPet(lua_State* L, Player* player)
    {
        bool show = ALE::CHECKVAL<bool>(L, 2);
        player->SetShowDKPet(show);
        return 0;
    }

    /**
     * Sets the [Player]'s viewpoint to the specified target.
     *
     * @param [WorldObject] target : the object to view from
     */
    int SetViewpoint(lua_State* L, Player* player)
    {
        WorldObject* target = ALE::CHECKOBJ<WorldObject>(L, 2);
        bool apply = ALE::CHECKVAL<bool>(L, 3, false);
        player->SetViewpoint(target, apply);
        return 0;
    }

    /**
     * Toggles instant flight mode for the [Player].
     */
    int ToggleInstantFlight(lua_State* /*L*/, Player* player)
    {
        player->ToggleInstantFlight();
        return 0;
    }

    /**
     * Returns the [Player]'s character creation time.
     *
     * @return uint32 creationTime : Unix timestamp of character creation
     */
    int GetCreationTime(lua_State* L, Player* player)
    {
        ALE::Push(L, static_cast<uint32>(player->GetCreationTime().count()));
        return 1;
    }

    /**
     * Sets the [Player]'s character creation time.
     *
     * @param uint32 creationTime : Unix timestamp to set as creation time
     */
    int SetCreationTime(lua_State* L, Player* player)
    {
        uint32 creationTime = ALE::CHECKVAL<uint32>(L, 2);
        player->SetCreationTime(Seconds(creationTime));
        return 0;
    }

    /**
     * Returns the [Player]'s dodge chance from agility.
     *
     * @return float dodgeChance : dodge percentage from agility stat
     */
    int GetDodgeFromAgility(lua_State* L, Player* player)
    {
        float diminishing, nondiminishing;
        player->GetDodgeFromAgility(diminishing, nondiminishing);
        ALE::Push(L, diminishing + nondiminishing);
        return 1;
    }

    /**
     * Returns the [Player]'s melee critical hit chance from agility.
     *
     * @return float critChance : melee crit percentage from agility stat
     */
    int GetMeleeCritFromAgility(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetMeleeCritFromAgility());
        return 1;
    }

    /**
     * Returns the [Player]'s spell critical hit chance from intellect.
     *
     * @return float critChance : spell crit percentage from intellect stat
     */
    int GetSpellCritFromIntellect(lua_State* L, Player* player)
    {
        ALE::Push(L, player->GetSpellCritFromIntellect());
        return 1;
    }

    /**
     * Returns an item from the [Player]'s inventory by slot.
     *
     * @param uint32 slot : inventory slot number
     * @return [Item] item : the item in the specified slot or nil
     */
    int GetInventoryItem(lua_State* L, Player* player)
    {
        uint32 slot = ALE::CHECKVAL<uint32>(L, 2);
        if (slot >= INVENTORY_SLOT_ITEM_END)
            return 1;
        ALE::Push(L, player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot));
        return 1;
    }

    /**
     * Returns an item from the [Player]'s bank by slot.
     *
     * @param uint32 slot : bank slot number
     * @return [Item] item : the item in the specified bank slot or nil
     */
    int GetBankItem(lua_State* L, Player* player)
    {
        uint32 slot = ALE::CHECKVAL<uint32>(L, 2);
        if (slot >= BANK_SLOT_ITEM_END)
            return 1;
        ALE::Push(L, player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot + BANK_SLOT_ITEM_START));
        return 1;
    }
};
#endif

